├── .github └── workflows │ └── rust.yml ├── .gitmodules ├── Cargo.toml ├── LICENSE ├── README.md ├── cli ├── Cargo.toml ├── README.md └── src │ ├── commands │ ├── ble │ │ ├── bearers.rs │ │ ├── hci │ │ │ ├── adapters.rs │ │ │ ├── dump.rs │ │ │ ├── mod.rs │ │ │ └── pcap.rs │ │ ├── mod.rs │ │ └── remote.rs │ ├── crypto.rs │ ├── mod.rs │ ├── provisioner.rs │ └── state.rs │ ├── helper.rs │ └── main.rs ├── docs ├── mesh_layout.PNG ├── mesh_net_incoming_flowchart.PNG └── mesh_stack.png └── src ├── access.rs ├── address.rs ├── advertisement.rs ├── beacon.rs ├── control.rs ├── crypto ├── aes.rs ├── aes_ccm.rs ├── aes_cmac.rs ├── ecdh.rs ├── k_funcs.rs ├── key.rs ├── materials.rs ├── mod.rs └── nonce.rs ├── device_state.rs ├── foundation ├── element.rs ├── health.rs ├── mod.rs ├── model.rs ├── publication.rs └── state.rs ├── friend.rs ├── interface.rs ├── lib.rs ├── lower.rs ├── mesh.rs ├── mesh_io.rs ├── models ├── config │ ├── messages.rs │ └── mod.rs ├── generics │ └── mod.rs ├── lighting │ └── mod.rs ├── mod.rs ├── model.rs ├── sensors │ └── mod.rs ├── state.rs ├── time │ └── mod.rs └── transition.rs ├── net.rs ├── properties ├── characteristics.rs ├── mod.rs ├── property.rs └── value.rs ├── provisioning ├── beacons.rs ├── bearer.rs ├── bearer_control.rs ├── confirmation.rs ├── data.rs ├── generic.rs ├── generic_bearer.rs ├── generic_link.rs ├── link.rs ├── mod.rs ├── pb_adv.rs ├── pb_gatt.rs ├── protocol.rs └── provisioner.rs ├── random.rs ├── reassembler.rs ├── relay.rs ├── replay.rs ├── samples.rs ├── segmenter.rs ├── stack ├── bearer.rs ├── bearers │ ├── advertiser.rs │ └── mod.rs ├── element.rs ├── full.rs ├── incoming.rs ├── messages.rs ├── mod.rs ├── model.rs ├── outgoing.rs ├── segments.rs ├── transport.rs └── watchdog.rs ├── timestamp.rs └── upper.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Checkout Submodules 13 | run: git submodule update --init --recursive 14 | - name: Install Rust stable 15 | run: | 16 | rustup toolchain update --no-self-update stable 17 | rustup default stable 18 | - name: Build 19 | run: cargo build --verbose 20 | - name: Run tests 21 | run: cargo test --verbose 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "btle"] 2 | path = btle 3 | url = https://github.com/AndrewGi/btle.git 4 | [submodule "async_driver"] 5 | path = async_driver 6 | url = https://github.com/AndrewGi/async_driver.git 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bluetooth_mesh" 3 | description = """ 4 | Cross-platform, full Bluetooth Mesh stack implemented in Rust. Following the Bluetooth Mesh Spec Core v1.0 by SIG. 5 | 6 | Designed to work with any almost any BLE radio (uses https://github.com/AndrewGi/btle/ for 7 | platform dependent Bluetooth drivers). While a stack is provided by the library, all the primatives and objects needed 8 | to customize and create your own stack are provided. 9 | 10 | See https://github.com/AndrewGi/BluetoothMeshRust for more. 11 | """ 12 | license = "GPL-3.0-only" 13 | repository = "https://github.com/AndrewGi/BluetoothMeshRust" 14 | version = "0.1.4" 15 | authors = ["Andrew Gilbrough "] 16 | edition = "2018" 17 | readme = "README.md" 18 | 19 | [badges] 20 | maintenance = {status ="actively-developed"} 21 | 22 | [features] 23 | default = ["full_stack"] 24 | full_stack = ["std", "driver_async/tokio_asyncs", "futures-util"] 25 | serde-1 = ["serde", "btle/serde-1"] 26 | std = ["serde/std", "rand/std", "btle/std", "ring/std"] 27 | 28 | [dependencies] 29 | # Custom backends built for `bluetooth_mesh` 30 | btle = {version = "0.1.4", path = "btle", default-features = false, features=["hci"]} 31 | driver_async = {version = "0.0.3", path="async_driver", default-features = false} 32 | # Common libraries 33 | serde = {version = "1.0", default-features = false, features = ["derive"], optional = true } 34 | futures-util = {version = "0.3.8", optional=true, default-features = false, features=["alloc"]} 35 | # Used mostly for crypto key generation 36 | rand = {version ="0.7", default-features = false} 37 | # Crypto Libs 38 | aes = "0.6" 39 | cmac = "0.5" 40 | aead = "0.3" 41 | dbl = "0.3" 42 | subtle = "2.3" 43 | block-modes = "0.7" 44 | ring = "0.17.0-alpha.4" 45 | # Most crypto libs take generic-array inputs 46 | generic-array = "0.14" 47 | typenum = "1.12" 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth Mesh Rust 2 | [![crates.io](https://img.shields.io/crates/v/bluetooth_mesh)](https://crates.io/crates/bluetooth_mesh) 3 | [![docs.rs](https://docs.rs/bluetooth_mesh/badge.svg)](https://docs.rs/bluetooth_mesh) 4 | 5 | 6 | !! WIP and API not stable until version 1.0 !! 7 | 8 | Cross-platform, full Bluetooth Mesh stack implemented in Rust. Following the Bluetooth Mesh Spec Core v1.0 by SIG. Designed to work with any almost any BLE radio (uses https://github.com/AndrewGi/btle/ for platform dependent Bluetooth drivers). While a stack is provided by the library, all the primitives and objects needed to customize and create your own stack are provided. 9 | 10 | This library is designed for `#![no_std]` in mind. However, because of the complexity of the Bluetooth Mesh Stack, `std` is required for the `full_stack` which uses async tokio for message handling and processing. `#![no_std]` is also disabled for now until https://github.com/rust-lang/rust/pull/69033 hits nightly/stable. 11 | 12 | The only heap allocations made during processing a message is allocating memory for the message at the access layer. Most Mesh PDUs are <31 bytes (to fit in a single BLE Advertisement) so the Network and Lower Transport Layer stores its data statically on the stack. Upper PDUs and above allow for allocation elsewhere than the stack (Upper Transport PDUs can be up to 380 bytes!) but a custom allocator/storage for the PDU can be genericly provided. 13 | 14 | ## Examples 15 | See [Mesh CLI](/cli/) for an application example. 16 | 17 | ## How the Stack works 18 | ![The flowchart of the full mesh stack](/docs/mesh_stack.png) 19 | -------------------------------------------------------------------------------- /cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mesh_cli" 3 | version = "0.1.0" 4 | authors = ["AndrewGi "] 5 | edition = "2018" 6 | readme = "README.md" 7 | 8 | [badges] 9 | maintenance = {status ="actively-developed"} 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [features] 14 | default = ["mesh", "pcap", "btle_usb", "btle_bluez"] 15 | btle_usb = ["btle/hci_usb"] 16 | btle_bluez = ["btle/bluez_socket"] 17 | mesh = ["bluetooth_mesh"] 18 | pcap = ["pcap-file"] 19 | 20 | [dependencies] 21 | bluetooth_mesh = {path = "../", features=["full_stack", "serde-1"], optional = true} 22 | clap = "2.33.0" 23 | serde_json = "1.0" 24 | slog = "2.5.2" 25 | slog-term = "2.4.2" 26 | tokio = {version = "0.3", features=["net", "time", "rt"]} 27 | futures-core = {version = "0.3", default_features = false} 28 | futures-io = {version = "0.3", default_features = false} 29 | futures-util = {version = "0.3", default_features = false} 30 | driver_async = {version = "0.0.3", path="../async_driver"} 31 | pcap-file = {version = "1.1.1", optional = true} 32 | libc = "0.2.69" 33 | btle = {path = "../btle", features= ["std"]} 34 | usbw = {path = "../../usbw", version="0.0.2"} 35 | once_cell = "1.4" -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | # Bluetooth Mesh CLI 2 | This is a command line utility to control and interface with Bluetooth Mesh Networks. While not all devices have supporting bearers, 3 | they can still run utilitiy commands to generate keys, read device_state files and to encrypt/decrypt messages. 4 | 5 | See `cargo run -- --help` for more help/info. 6 | 7 | ## Sub Commands 8 | - `crypto` Read/Write/Generate crypto keys 9 | - `provisioner` Act as a provisioner in a Mesh Network (requires bearer) (not finished) 10 | - `generate` Generate new `device_state.json` file 11 | - Many more to come 12 | -------------------------------------------------------------------------------- /cli/src/commands/ble/bearers.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cli/src/commands/ble/hci/adapters.rs: -------------------------------------------------------------------------------- 1 | use crate::{helper, CLIError}; 2 | use btle::hci::usb; 3 | pub fn sub_command() -> clap::App<'static, 'static> { 4 | clap::SubCommand::with_name("adapters").about("list possible HCI adapters") 5 | } 6 | pub fn list_possible_adapters() -> Result<(), CLIError> { 7 | list_usb_adapters() 8 | } 9 | #[cfg(feature = "btle_usb")] 10 | pub fn list_usb_adapters() -> Result<(), CLIError> { 11 | for adapter in 12 | usb::device::bluetooth_adapters(helper::libusb_context().context_ref().device_list().iter()) 13 | { 14 | println!("USB Adapter: {:?}", &adapter); 15 | } 16 | Ok(()) 17 | } 18 | #[cfg(not(feature = "btle_usb"))] 19 | pub fn list_usb_adapters() -> Result<(), CLIError> { 20 | println!("USB HCI not enabled"); 21 | Ok(()) 22 | } 23 | -------------------------------------------------------------------------------- /cli/src/commands/ble/hci/dump.rs: -------------------------------------------------------------------------------- 1 | use crate::helper; 2 | use crate::CLIError; 3 | use btle::le::report::ReportInfo; 4 | use futures_util::StreamExt; 5 | use std::pin::Pin; 6 | 7 | pub fn sub_command() -> clap::App<'static, 'static> { 8 | clap::SubCommand::with_name("dump") 9 | .about("dump raw HCI data to the console") 10 | .arg( 11 | clap::Arg::with_name("source") 12 | .help("HCI source/sink (`bluez`/`usb`)") 13 | .short("s") 14 | .long("source") 15 | .value_name("SOURCE_NAME:ADAPTER_ID") 16 | .default_value("usb:0"), 17 | ) 18 | .arg( 19 | clap::Arg::with_name("pcap") 20 | .help("dump the BLE advertisements to a pcap file/pipe") 21 | .short("p") 22 | .long("pcap") 23 | .value_name("PCAP_FILE"), 24 | ) 25 | } 26 | 27 | pub fn dump_matches( 28 | parent_logger: &slog::Logger, 29 | dump_matches: &clap::ArgMatches, 30 | ) -> Result<(), CLIError> { 31 | let logger = parent_logger.new(o!()); 32 | info!(logger, "dump"); 33 | let pcap_file = dump_matches.value_of("pcap"); 34 | let source = dump_matches.value_of("source").expect("required by clap"); 35 | match dump_matches.subcommand() { 36 | ("", _) => dump(&logger, source, pcap_file), 37 | _ => unreachable!("unhandled subcommand"), 38 | } 39 | } 40 | pub fn dump( 41 | _: &slog::Logger, 42 | which_adapter: &'_ str, 43 | pcap_file: Option<&'_ str>, 44 | ) -> Result<(), CLIError> { 45 | crate::helper::tokio_runtime().block_on(dump_adapter_pcap(which_adapter, pcap_file)) 46 | } 47 | pub async fn dump_adapter_pcap( 48 | which_adapter: &'_ str, 49 | pcap_file: Option<&'_ str>, 50 | ) -> Result<(), CLIError> { 51 | let adapter = helper::hci_adapter(which_adapter).await?; 52 | println!("using adapter `{:?}`", adapter); 53 | match pcap_file { 54 | Some(pcap_file) => { 55 | println!("using pcap file '{}'", pcap_file); 56 | dump_adapter(super::pcap::PcapAdapter::open(adapter, pcap_file)?).await 57 | } 58 | None => dump_adapter(adapter).await, 59 | } 60 | } 61 | pub async fn dump_adapter(adapter: A) -> Result<(), CLIError> { 62 | let adapter = btle::hci::adapters::Adapter::new(adapter); 63 | let mut le = adapter.le(); 64 | println!("resetting adapter..."); 65 | le.adapter.reset().await?; 66 | println!("settings scan parameters..."); 67 | // Set BLE Scan parameters (when to scan, how long, etc) 68 | le.set_scan_parameters(btle::le::scan::ScanParameters::DEFAULT) 69 | .await?; 70 | // Enable scanning for advertisement packets. 71 | le.set_scan_enable(true, false).await?; 72 | 73 | println!("waiting for advertisements..."); 74 | // Create the advertisement stream from the LEAdapter. 75 | let mut stream = le.advertisement_stream::>().await?; 76 | // Pin it. 77 | let mut stream = unsafe { Pin::new_unchecked(&mut stream) }; 78 | loop { 79 | // Asynchronously iterate through the stream and print each advertisement report. 80 | while let Some(report) = stream.next().await { 81 | println!("report: {:?}", &report); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cli/src/commands/ble/hci/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::CLIError; 2 | 3 | pub mod adapters; 4 | pub mod dump; 5 | pub mod pcap; 6 | pub fn sub_command() -> clap::App<'static, 'static> { 7 | clap::SubCommand::with_name("hci") 8 | .about("interact with Bluetooth HCI (Host Controller Interface)") 9 | .subcommand(dump::sub_command()) 10 | .subcommand(adapters::sub_command()) 11 | } 12 | 13 | pub fn hci_matches( 14 | parent_logger: &slog::Logger, 15 | ble_matches: &clap::ArgMatches, 16 | ) -> Result<(), CLIError> { 17 | let subcommand_str = ble_matches.subcommand_name().unwrap_or(""); 18 | let logger = parent_logger.new(o!("sub_command" => subcommand_str.to_owned())); 19 | debug!(logger, "hci_matches"); 20 | match ble_matches.subcommand() { 21 | ("dump", Some(dump_matches)) => dump::dump_matches(&logger, dump_matches), 22 | ("adapters", _) => adapters::list_possible_adapters(), 23 | _ => Err(CLIError::Clap(clap::Error::with_description( 24 | "missing sub_command", 25 | clap::ErrorKind::ArgumentNotFound, 26 | ))), 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cli/src/commands/ble/hci/pcap.rs: -------------------------------------------------------------------------------- 1 | use crate::CLIError; 2 | use btle::error::IOError; 3 | use btle::hci::command::CommandPacket; 4 | use futures_core::future::LocalBoxFuture; 5 | use std::convert::TryInto; 6 | 7 | pub struct PcapAdapter { 8 | pub adapter: A, 9 | pub pcap_writer: pcap_file::PcapWriter, 10 | } 11 | impl PcapAdapter { 12 | pub fn open>(adapter: A, path: P) -> Result { 13 | Ok(PcapAdapter { 14 | adapter, 15 | pcap_writer: { 16 | let header = pcap_file::pcap::PcapHeader { 17 | datalink: pcap_file::DataLink::BLUETOOTH_HCI_H4, 18 | ..pcap_file::pcap::PcapHeader::default() 19 | }; 20 | let file = std::fs::OpenOptions::new() 21 | .create(true) 22 | .write(true) 23 | .open(path) 24 | .map_err(|e| CLIError::IOError("io error opening pcap file".to_owned(), e))?; 25 | pcap_file::PcapWriter::with_header(header, file).map_err(|e| { 26 | CLIError::OtherMessage(format!("error opening pcap writer: {}", e)) 27 | })? 28 | }, 29 | }) 30 | } 31 | pub fn dump_packet( 32 | &mut self, 33 | packet: btle::hci::packet::RawPacket<&[u8]>, 34 | ) -> Result<(), Box> { 35 | // TODO: Reuse same buffer 36 | let out = packet 37 | .pack::>() 38 | .expect("Box should be able to hold any packet"); 39 | let time = std::time::SystemTime::now() 40 | .duration_since(std::time::UNIX_EPOCH) 41 | .map_err(|_| { 42 | CLIError::OtherMessage("time set before UNIX_EPOCH, can't save pcap".to_owned()) 43 | })?; 44 | let ts_sec = time.as_secs().try_into().map_err(|_| { 45 | CLIError::OtherMessage("time overflow error (u64->i32), can't save pcap".to_owned()) 46 | })?; 47 | let ts_nsec = time.subsec_nanos().try_into().map_err(|_| { 48 | CLIError::OtherMessage("time overflow error (u32->i32), can't save pcap".to_owned()) 49 | })?; 50 | let len = out 51 | .as_ref() 52 | .len() 53 | .try_into() 54 | .expect("all HCI packets should be smaller than u32::MAX"); 55 | self.pcap_writer 56 | .write(ts_sec, ts_nsec, out.as_ref(), len) 57 | .map_err(|e| CLIError::OtherMessage(format!("error writing pcap: {}", e)))?; 58 | Ok(()) 59 | } 60 | } 61 | 62 | impl btle::hci::adapter::Adapter for PcapAdapter { 63 | fn write_command<'s, 'p: 's>( 64 | &'s mut self, 65 | packet: CommandPacket<&'p [u8]>, 66 | ) -> LocalBoxFuture<'s, Result<(), btle::hci::adapter::Error>> { 67 | Box::pin(async move { 68 | self.dump_packet(packet.to_raw_packet::>().as_ref()) 69 | .map_err(|_| btle::hci::adapter::Error::IOError(IOError::Other))?; 70 | self.adapter.write_command(packet).await 71 | }) 72 | } 73 | 74 | fn read_event<'s, 'p: 's, S: btle::bytes::Storage + 'p>( 75 | &'s mut self, 76 | ) -> LocalBoxFuture<'s, Result, btle::hci::adapter::Error>> 77 | { 78 | Box::pin(async move { 79 | let event: btle::hci::event::EventPacket = self.adapter.read_event().await?; 80 | self.dump_packet(event.to_raw_packet::>().as_ref()) 81 | .map_err(|_| btle::hci::adapter::Error::IOError(IOError::Other))?; 82 | Ok(event) 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cli/src/commands/ble/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::CLIError; 2 | 3 | pub mod bearers; 4 | pub mod hci; 5 | pub mod remote; 6 | 7 | pub fn sub_command() -> clap::App<'static, 'static> { 8 | clap::SubCommand::with_name("ble") 9 | .about("interact directly with the BLE driver") 10 | .subcommand(hci::sub_command()) 11 | } 12 | 13 | pub fn ble_matches( 14 | parent_logger: &slog::Logger, 15 | ble_matches: &clap::ArgMatches, 16 | ) -> Result<(), CLIError> { 17 | let logger = parent_logger.new(o!()); 18 | match ble_matches.subcommand() { 19 | ("hci", Some(hci_matches)) => hci::hci_matches(&logger, hci_matches), 20 | ("", None) => Err(CLIError::Clap(clap::Error::with_description( 21 | "missing ble subcommand", 22 | clap::ErrorKind::ArgumentNotFound, 23 | ))), 24 | _ => unreachable!("unhandled ble subcommand"), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cli/src/commands/ble/remote.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /cli/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ble; 2 | #[cfg(feature = "mesh")] 3 | pub mod crypto; 4 | #[cfg(feature = "mesh")] 5 | pub mod provisioner; 6 | #[cfg(feature = "mesh")] 7 | pub mod state; 8 | -------------------------------------------------------------------------------- /cli/src/commands/provisioner.rs: -------------------------------------------------------------------------------- 1 | use crate::helper::tokio_runtime; 2 | use crate::CLIError; 3 | use bluetooth_mesh::provisioning::link::Link; 4 | use bluetooth_mesh::provisioning::pb_adv; 5 | use bluetooth_mesh::random::Randomizable; 6 | use bluetooth_mesh::replay; 7 | use bluetooth_mesh::stack::bearer::{IncomingMessage, OutgoingMessage, PBAdvBuf}; 8 | use bluetooth_mesh::stack::bearers::advertiser::BufferedHCIAdvertiser; 9 | use bluetooth_mesh::stack::full::FullStack; 10 | use bluetooth_mesh::stack::StackInternals; 11 | use bluetooth_mesh::uuid::UUID; 12 | use driver_async::asyncs::sync::mpsc; 13 | use driver_async::asyncs::task; 14 | use futures_util::stream::{Stream, StreamExt}; 15 | pub fn sub_command() -> clap::App<'static, 'static> { 16 | clap::SubCommand::with_name("provisioner") 17 | .about("Provisioner Role for adding Nodes to a network") 18 | .subcommand( 19 | clap::SubCommand::with_name("run") 20 | .about("join real Bluetooth Mesh network as a provisioner.") 21 | .arg( 22 | clap::Arg::with_name("source") 23 | .help("HCI source/sink (`bluez`/`usb`)") 24 | .short("s") 25 | .long("source") 26 | .value_name("SOURCE_NAME:ADAPTER_ID") 27 | .default_value("usb:0"), 28 | ), 29 | ) 30 | } 31 | pub fn provisioner_matches( 32 | logger: &slog::Logger, 33 | device_state_path: &str, 34 | matches: &clap::ArgMatches, 35 | ) -> Result<(), CLIError> { 36 | let mut runtime = tokio_runtime(); 37 | match matches.subcommand() { 38 | ("run", Some(run_matches)) => tokio::task::LocalSet::new().block_on( 39 | &mut runtime, 40 | provision( 41 | logger, 42 | run_matches.value_of("source").expect("required by clap"), 43 | device_state_path, 44 | ), 45 | ), 46 | ("", None) => Err(CLIError::Clap(clap::Error::with_description( 47 | "missing subcommand", 48 | clap::ErrorKind::ArgumentNotFound, 49 | ))), 50 | _ => unreachable!("unhandled provisioner subcommand"), 51 | } 52 | } 53 | async fn filter_only_pb_adv< 54 | S: Stream> + Unpin, 55 | >( 56 | mut stream: S, 57 | ) -> Result>, btle::hci::adapter::Error> { 58 | while let Some(msg) = stream.next().await { 59 | if let Some(pb_adv_pdu) = msg?.pb_adv() { 60 | return Ok(Some(pb_adv_pdu)); 61 | } 62 | } 63 | Ok(None) 64 | } 65 | pub async fn dump() -> Result<(), CLIError> { 66 | unimplemented!() 67 | } 68 | pub async fn provision( 69 | _logger: &slog::Logger, 70 | which_adapter: &'_ str, 71 | device_state_path: &str, 72 | ) -> Result<(), CLIError> { 73 | const BEARER_CHANNEL_SIZE: usize = 16; 74 | let dsm = crate::helper::load_device_state(device_state_path)?; 75 | println!("opening HCI adapter..."); 76 | let adapter = crate::helper::hci_adapter(which_adapter).await?; 77 | println!("HCI adapter (`{:?}`) open!", adapter); 78 | // TODO: I wrote a async usb driver (`usbw`) so a BufferedHCIAdvertiser might not be needed anymore! 79 | let (mut adapter, mut bearer_rx, bearer_tx) = 80 | BufferedHCIAdvertiser::new_with_channel_size(adapter, BEARER_CHANNEL_SIZE); 81 | let _adapter_task = task::spawn(async move { 82 | adapter.run_loop_send_error().await; 83 | }); 84 | async move { 85 | let early_end_error = 86 | || CLIError::OtherMessage("early end on incoming advertisement stream".to_owned()); 87 | println!("starting buffered advertiser..."); 88 | let internals = StackInternals::new(dsm); 89 | let cache = replay::Cache::new(); 90 | let stack = FullStack::new(internals, cache, 5); 91 | // Box<[u8]> stores the PDU being assembled for the pb-adv link. 92 | println!("waiting for beacons..."); 93 | let beacon = loop { 94 | if let Some(beacon) = bearer_rx.recv().await.ok_or(early_end_error())??.beacon() { 95 | if beacon.beacon.unprovisioned().is_some() { 96 | break beacon; 97 | } 98 | } 99 | }; 100 | println!("using beacon: `{:?}`", &beacon); 101 | let uuid: UUID = beacon.beacon.unprovisioned().expect("filtered above").uuid; 102 | println!("UUID: {}", &uuid); 103 | let (tx_link, mut rx_link) = mpsc::channel(Link::>::CHANNEL_SIZE); 104 | let mut bearer_link_tx = bearer_tx.clone(); 105 | // Forwards PB ADV Link messages to the bearer 106 | task::spawn(async move { 107 | while let Some(msg) = rx_link.recv().await { 108 | dbg!(&msg); 109 | if bearer_link_tx 110 | .send(OutgoingMessage::PBAdv(msg)) 111 | .await 112 | .is_err() 113 | { 114 | return; 115 | } 116 | } 117 | }); 118 | 119 | let mut link = Link::::invite(tx_link, pb_adv::LinkID::random(), &uuid); 120 | let next_pb_adv = move || async move { 121 | Result::<_, Box>::Ok( 122 | filter_only_pb_adv(&mut bearer_rx) 123 | .await? 124 | .ok_or_else(early_end_error)? 125 | .pdu, 126 | ) 127 | }; 128 | link.handle_pb_adv_pdu(next_pb_adv().await?.as_ref()) 129 | .await?; 130 | println!("{:?}", link.state()); 131 | Result::<(), Box>::Ok(()) 132 | } 133 | .await 134 | .map_err(|e| CLIError::OtherMessage(format!("stack error: {:?}", e)))?; 135 | println!("provisioner done"); 136 | Ok(()) 137 | } 138 | -------------------------------------------------------------------------------- /cli/src/commands/state.rs: -------------------------------------------------------------------------------- 1 | use crate::{helper, CLIError}; 2 | use bluetooth_mesh::address::{Address, UnicastAddress}; 3 | use bluetooth_mesh::device_state; 4 | use bluetooth_mesh::mesh::ElementCount; 5 | use std::str::FromStr; 6 | 7 | pub fn sub_command() -> clap::App<'static, 'static> { 8 | clap::SubCommand::with_name("state").subcommand( 9 | clap::SubCommand::with_name("new") 10 | .about("Generate a device state with desired parameters") 11 | .arg( 12 | clap::Arg::with_name("element_count") 13 | .short("c") 14 | .value_name("ELEMENT_COUNT") 15 | .required(true) 16 | .default_value("1") 17 | .validator(|count| { 18 | if let Ok(c) = usize::from_str(&count) { 19 | match c { 20 | 1..=0xFF => Ok(()), 21 | _ => Err(format!( 22 | "Invalid element count '{}'. Expected in range [1..0xFF]", 23 | c 24 | )), 25 | } 26 | } else { 27 | Err(format!("Invalid element count '{}'. Not a number", count)) 28 | } 29 | }), 30 | ) 31 | .arg( 32 | clap::Arg::with_name("element_address") 33 | .short("a") 34 | .value_name("UNICAST_ADDRESS") 35 | .required(true) 36 | .default_value("1") 37 | .validator(|address| { 38 | let radix = if address.starts_with("0x") { 16 } else { 10 }; 39 | if let Ok(a) = u16::from_str_radix(address.trim_start_matches("0x"), radix) 40 | { 41 | match Address::from(a) { 42 | Address::Unicast(_) => Ok(()), 43 | _ => Err(format!("Non-unicast address '{}' given", &address)), 44 | } 45 | } else { 46 | Err(format!("Non-address '{}' given", &address)) 47 | } 48 | }), 49 | ) 50 | .arg( 51 | clap::Arg::with_name("default_ttl") 52 | .short("t") 53 | .value_name("DEFAULT_TTL") 54 | .validator(helper::is_ttl), 55 | ), 56 | ) 57 | } 58 | pub fn state_matches( 59 | parent_logger: &slog::Logger, 60 | device_state_path: &str, 61 | gen_matches: &clap::ArgMatches, 62 | ) -> Result<(), CLIError> { 63 | match gen_matches.subcommand() { 64 | ("new", Some(new_matches)) => { 65 | match ( 66 | new_matches.value_of("element_count"), 67 | new_matches.value_of("element_address"), 68 | ) { 69 | (Some(element_count), Some(element_address)) => { 70 | let count = ElementCount(element_count.parse().expect("checked by clap")); 71 | let address = 72 | UnicastAddress::new(element_address.parse().expect("checked by clap")); 73 | generate(parent_logger, device_state_path, address, count) 74 | } 75 | _ => unreachable!("element count and element address should have default values"), 76 | } 77 | } 78 | 79 | ("", None) => Err(CLIError::Clap(clap::Error::with_description( 80 | "missing state subcommand", 81 | clap::ErrorKind::ArgumentNotFound, 82 | ))), 83 | _ => unreachable!("unhandled state subcommand"), 84 | } 85 | } 86 | pub fn generate( 87 | parent_logger: &slog::Logger, 88 | device_state_path: &str, 89 | primary_address: UnicastAddress, 90 | element_count: ElementCount, 91 | ) -> Result<(), CLIError> { 92 | let logger = parent_logger.new(o!("device_state_path" => device_state_path.to_owned())); 93 | let f = helper::load_file(device_state_path, true, true)?; 94 | info!(logger, "found device_state"); 95 | let device_state = device_state::DeviceState::new(primary_address, element_count); 96 | serde_json::to_writer(f, &device_state).map_err(CLIError::SerdeJSON)?; 97 | Ok(()) 98 | } 99 | -------------------------------------------------------------------------------- /cli/src/helper.rs: -------------------------------------------------------------------------------- 1 | use crate::CLIError; 2 | #[cfg(feature = "mesh")] 3 | use bluetooth_mesh::{device_state, mesh}; 4 | use btle::bytes::Storage; 5 | use btle::hci::adapter::Adapter; 6 | use btle::hci::command::CommandPacket; 7 | use btle::hci::event::EventPacket; 8 | use futures_core::future::LocalBoxFuture; 9 | use std::convert::TryFrom; 10 | use std::fmt::{Error, Formatter}; 11 | use std::str::FromStr; 12 | use usbw::libusb::asyncs::AsyncContext; 13 | 14 | pub struct HexSlice<'a>(pub &'a [u8]); 15 | impl<'a> std::fmt::UpperHex for HexSlice<'a> { 16 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 17 | for &b in self.0 { 18 | write!(f, "{:X}", b)?; 19 | } 20 | Ok(()) 21 | } 22 | } 23 | 24 | impl<'a> std::fmt::LowerHex for HexSlice<'a> { 25 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 26 | for &b in self.0 { 27 | write!(f, "{:02x}", b)?; 28 | } 29 | Ok(()) 30 | } 31 | } 32 | pub fn is_hex_str(s: &str) -> bool { 33 | if s.len() % 2 == 1 { 34 | return false; 35 | } 36 | for c in s.chars() { 37 | if !c.is_digit(16) { 38 | return false; 39 | } 40 | } 41 | return true; 42 | } 43 | pub fn is_128_bit_hex_str_validator(input: String) -> Result<(), String> { 44 | if input.len() == 32 && is_hex_str(&input) { 45 | Ok(()) 46 | } else { 47 | Err(format!("'{}' is not a 128-bit hex string", &input)) 48 | } 49 | } 50 | #[cfg(feature = "mesh")] 51 | pub fn is_ttl(input: String) -> Result<(), String> { 52 | let error_msg = || Err(format!("`{}` is not a valid TTL", &input)); 53 | match u8::from_str(&input) { 54 | Ok(v) => match mesh::TTL::try_from(v) { 55 | Ok(_) => Ok(()), 56 | Err(_) => error_msg(), 57 | }, 58 | Err(_) => error_msg(), 59 | } 60 | } 61 | pub fn is_u8_validator(input: String) -> Result<(), String> { 62 | match u8::from_str(&input) { 63 | Ok(_) => Ok(()), 64 | Err(_) => Err(format!("'{}' is not a 8-bit unsigned integer", &input)), 65 | } 66 | } 67 | pub fn is_u16_validator(input: String) -> Result<(), String> { 68 | match u16::from_str(&input) { 69 | Ok(_) => Ok(()), 70 | Err(_) => Err(format!("'{}' is not a 16-bit unsigned integer", &input)), 71 | } 72 | } 73 | #[cfg(feature = "mesh")] 74 | pub fn is_u24_validator(input: String) -> Result<(), String> { 75 | match u32::from_str(&input) 76 | .ok() 77 | .and_then(|v| bluetooth_mesh::mesh::U24::try_from(v).ok()) 78 | { 79 | Some(_) => Ok(()), 80 | None => Err(format!("'{}' is not a 24-bit unsigned integer", &input)), 81 | } 82 | } 83 | pub fn is_u32_validator(input: String) -> Result<(), String> { 84 | match u32::from_str(&input) { 85 | Ok(_) => Ok(()), 86 | Err(_) => Err(format!("'{}' is not a 32-bit unsigned integer", &input)), 87 | } 88 | } 89 | pub fn hex_str_to_bytes>(s: &str) -> Option { 90 | let mut out = T::default(); 91 | if s.len() != out.as_mut().len() * 2 || out.as_mut().len() == 0 { 92 | None 93 | } else { 94 | { 95 | let buf = out.as_mut(); 96 | for (i, c) in s.chars().enumerate() { 97 | let v = u8::try_from(c.to_digit(16)?).expect("only returns [0..=15]"); 98 | buf[i / 2] |= v << u8::try_from(((i + 1) % 2) * 4).expect("only returns 0 or 4"); 99 | } 100 | } 101 | Some(out) 102 | } 103 | } 104 | pub fn is_bool_validator(input: String) -> Result<(), String> { 105 | bool::from_str(&input) 106 | .ok() 107 | .map(|_| ()) 108 | .ok_or(format!("'{}' is not a valid bool", &input)) 109 | } 110 | pub fn load_file(path: &str, writeable: bool, create: bool) -> Result { 111 | std::fs::OpenOptions::new() 112 | .read(true) 113 | .write(writeable) 114 | .truncate(writeable) 115 | .create(create) 116 | .open(path) 117 | .map_err(|e| CLIError::IOError(path.to_owned(), e)) 118 | } 119 | #[cfg(feature = "mesh")] 120 | pub fn load_device_state(path: &str) -> Result { 121 | serde_json::from_reader(load_file(path, false, false)?).map_err(CLIError::SerdeJSON) 122 | } 123 | #[cfg(feature = "mesh")] 124 | pub fn write_device_state( 125 | path: &str, 126 | device_state: &device_state::DeviceState, 127 | ) -> Result<(), CLIError> { 128 | serde_json::to_writer_pretty(load_file(path, true, true)?, device_state) 129 | .map_err(CLIError::SerdeJSON) 130 | } 131 | pub fn tokio_runtime() -> tokio::runtime::Runtime { 132 | tokio::runtime::Builder::new_current_thread() 133 | .enable_all() 134 | .build() 135 | .expect("can't make async runtime") 136 | } 137 | #[derive(Debug)] 138 | pub enum HCIAdapter { 139 | Usb(btle::hci::usb::adapter::Adapter), 140 | #[cfg(not(all(unix, feature = "btle_bluez")))] 141 | BlueZ(btle::hci::adapter::DummyAdapter), 142 | #[cfg(all(unix, feature = "btle_bluez"))] 143 | BlueZ( 144 | btle::hci::stream::Stream< 145 | btle::hci::bluez_socket::AsyncHCISocket, 146 | Box, 147 | >, 148 | ), 149 | } 150 | impl Adapter for HCIAdapter { 151 | fn write_command<'s, 'p: 's>( 152 | &'s mut self, 153 | packet: CommandPacket<&'p [u8]>, 154 | ) -> LocalBoxFuture<'s, Result<(), btle::hci::adapter::Error>> { 155 | match self { 156 | HCIAdapter::Usb(u) => Adapter::write_command(u, packet), 157 | HCIAdapter::BlueZ(b) => Adapter::write_command(b, packet), 158 | } 159 | } 160 | fn read_event<'s, 'p: 's, S: Storage + 'p>( 161 | &'s mut self, 162 | ) -> LocalBoxFuture<'s, Result, btle::hci::adapter::Error>> { 163 | match self { 164 | HCIAdapter::Usb(u) => Adapter::read_event(u), 165 | HCIAdapter::BlueZ(b) => Adapter::read_event(b), 166 | } 167 | } 168 | } 169 | pub fn libusb_context() -> &'static AsyncContext { 170 | static CONTEXT: once_cell::sync::OnceCell = once_cell::sync::OnceCell::new(); 171 | CONTEXT.get_or_init(|| { 172 | let context = 173 | usbw::libusb::context::default_context().expect("unable to start libusb context"); 174 | context.set_debug_level(usbw::libusb::context::LogLevel::Info); 175 | context.start_async() 176 | }) 177 | } 178 | pub async fn usb_adapter(adapter_id: u16) -> Result { 179 | let context = libusb_context(); 180 | let device_list = context.context_ref().device_list(); 181 | let mut adapters = btle::hci::usb::device::bluetooth_adapters(device_list.iter()); 182 | let adapter = adapters 183 | .nth(adapter_id.into()) 184 | .ok_or_else(|| CLIError::OtherMessage("no usb bluetooth adapters found".to_owned()))?? 185 | .open() 186 | .map_err(|e: usbw::libusb::error::Error| match e { 187 | usbw::libusb::error::Error::NotSupported => CLIError::OtherMessage( 188 | "is the libusb driver installed for the USB adapter? (NotImplemented Error)" 189 | .to_owned(), 190 | ), 191 | e => CLIError::HCIAdapterError(btle::hci::adapter::Error::IOError( 192 | btle::hci::usb::Error::from(e).into(), 193 | )), 194 | })?; 195 | let mut adapter = btle::hci::usb::adapter::Adapter::open(context.make_async_device(adapter))?; 196 | adapter 197 | .flush_event_buffer() 198 | .await 199 | .map_err(btle::hci::usb::Error::from)?; 200 | Ok(adapter) 201 | } 202 | pub async fn hci_adapter(which_adapter: &str) -> Result { 203 | pub const USB_PREFIX: &'static str = "usb:"; 204 | pub const BLUEZ_PREFIX: &'static str = "bluez:"; 205 | if which_adapter.starts_with(USB_PREFIX) { 206 | Ok(HCIAdapter::Usb( 207 | usb_adapter((&which_adapter[USB_PREFIX.len()..]).parse().map_err(|_| { 208 | CLIError::OtherMessage(format!("bad usb adapter {}", which_adapter)) 209 | })?) 210 | .await?, 211 | )) 212 | } else if which_adapter.starts_with(BLUEZ_PREFIX) { 213 | Ok(HCIAdapter::BlueZ(bluez_adapter( 214 | (&which_adapter[BLUEZ_PREFIX.len()..]) 215 | .parse() 216 | .map_err(|_| { 217 | CLIError::OtherMessage(format!("bad bluez adapter {}", which_adapter)) 218 | })?, 219 | )?)) 220 | } else { 221 | Err(CLIError::OtherMessage(format!( 222 | "unrecognized HCI adapter `{}`", 223 | which_adapter 224 | ))) 225 | } 226 | } 227 | #[cfg(not(all(unix, feature = "btle_bluez")))] 228 | pub fn bluez_adapter(_: u16) -> Result { 229 | Err(CLIError::OtherMessage( 230 | "bluez either isn't enable or supported on this platform".to_owned(), 231 | )) 232 | } 233 | #[cfg(all(unix, feature = "btle_bluez"))] 234 | pub fn bluez_adapter( 235 | adapter_id: u16, 236 | ) -> Result< 237 | btle::hci::stream::Stream< 238 | btle::hci::bluez_socket::AsyncHCISocket, 239 | Box, 240 | >, 241 | CLIError, 242 | > { 243 | use btle::hci::bluez_socket; 244 | use core::convert::TryInto; 245 | let manager = bluez_socket::Manager::new() 246 | .map_err(|e| CLIError::HCIAdapterError(btle::hci::adapter::Error::IOError(e)))?; 247 | let socket = manager 248 | .get_adapter_socket(bluez_socket::AdapterID(adapter_id)) 249 | .map_err(|e| CLIError::HCIAdapterError(btle::hci::adapter::Error::IOError(e)))? 250 | .try_into() 251 | .map_err(|e| CLIError::IOError("unable to turn the bluez socket -> async".to_owned(), e))?; 252 | Ok(btle::hci::stream::Stream::new(Box::pin(socket))) 253 | } 254 | -------------------------------------------------------------------------------- /cli/src/main.rs: -------------------------------------------------------------------------------- 1 | use slog::Drain; 2 | #[macro_use] 3 | extern crate slog; 4 | 5 | use std::convert::TryFrom; 6 | pub mod commands; 7 | pub mod helper; 8 | #[derive(Debug)] 9 | pub enum CLIError { 10 | PermissionDenied, 11 | IOError(String, std::io::Error), 12 | Clap(clap::Error), 13 | SerdeJSON(serde_json::Error), 14 | OtherMessage(String), 15 | HCIAdapterError(btle::hci::adapter::Error), 16 | Other(Box), 17 | } 18 | impl btle::error::Error for CLIError {} 19 | impl From for CLIError { 20 | fn from(e: btle::hci::adapter::Error) -> Self { 21 | CLIError::HCIAdapterError(e) 22 | } 23 | } 24 | impl From for CLIError { 25 | fn from(e: btle::hci::usb::Error) -> Self { 26 | CLIError::HCIAdapterError(e.into()) 27 | } 28 | } 29 | #[cfg(feature = "mesh")] 30 | fn add_mesh_subcommands<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { 31 | app.subcommand(commands::state::sub_command()) 32 | .subcommand(commands::provisioner::sub_command()) 33 | .subcommand(commands::crypto::sub_command()) 34 | } 35 | #[cfg(not(feature = "mesh"))] 36 | fn add_mesh_subcommands<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { 37 | // `mesh` feature not enabled. Don't do anything. 38 | app 39 | } 40 | fn add_subcommands<'a, 'b>(app: clap::App<'a, 'b>) -> clap::App<'a, 'b> { 41 | add_mesh_subcommands(app.subcommand(commands::ble::sub_command())) 42 | } 43 | fn main() { 44 | let app = add_subcommands( 45 | clap::App::new("Bluetooth Mesh CLI") 46 | .version(clap::crate_version!()) 47 | .author("Andrew Gilbrough ") 48 | .about("Bluetooth Mesh Command Line Interface tool to interact with the Mesh") 49 | .arg( 50 | clap::Arg::with_name("verbose") 51 | .short("v") 52 | .long("verbose") 53 | .multiple(true) 54 | .max_values(5) 55 | .help("Set the amount of logging from level 0 up to level 5"), 56 | ) 57 | .arg( 58 | clap::Arg::with_name("device_state") 59 | .short("s") 60 | .long("device_state") 61 | .value_name("FILE") 62 | .help("Specifies device state .json file"), 63 | ), 64 | ); 65 | 66 | let matches = app.get_matches(); 67 | 68 | let _log_level = slog::Level::from_usize( 69 | 1 + usize::try_from(matches.occurrences_of("verbose")) 70 | .expect("verbose usize overflow (how??)"), 71 | ) 72 | .expect("verbose limit set too low"); 73 | let drain = slog_term::PlainSyncDecorator::new(std::io::stdout()); 74 | let root = slog::Logger::root(slog_term::FullFormat::new(drain).build().fuse(), slog::o!()); 75 | /* 76 | let root = slog::LevelFilter::new( 77 | slog::Logger::root(slog_term::FullFormat::new(drain).build().fuse(), slog::o!()), 78 | log_level, 79 | ); 80 | */ 81 | trace!(root, "main"); 82 | let sub_cmd = matches.subcommand().0; 83 | #[cfg(feature = "mesh")] 84 | let get_device_state_path = || -> &str { 85 | match matches.value_of("device_state") { 86 | Some(path) => path, 87 | None => clap::Error::with_description( 88 | "missing 'device_state.json` path", 89 | clap::ErrorKind::ArgumentNotFound, 90 | ) 91 | .exit(), 92 | } 93 | }; 94 | debug!(root, "arg_match"; "sub_command" => sub_cmd); 95 | if let Err(e) = (|| -> Result<(), CLIError> { 96 | match matches.subcommand() { 97 | ("", None) => error!(root, "no command given"), 98 | #[cfg(feature = "mesh")] 99 | ("state", Some(gen_matches)) => { 100 | commands::state::state_matches(&root, get_device_state_path(), gen_matches)? 101 | } 102 | #[cfg(feature = "mesh")] 103 | ("crypto", Some(crypto_matches)) => { 104 | commands::crypto::crypto_matches(&root, get_device_state_path(), crypto_matches)? 105 | } 106 | #[cfg(feature = "mesh")] 107 | ("provisioner", Some(prov_matches)) => commands::provisioner::provisioner_matches( 108 | &root, 109 | get_device_state_path(), 110 | prov_matches, 111 | )?, 112 | ("ble", Some(ble_matches)) => commands::ble::ble_matches(&root, ble_matches)?, 113 | _ => unreachable!("unhandled sub_command"), 114 | } 115 | debug!(root, "matches_done"); 116 | Ok(()) 117 | })() { 118 | match e { 119 | CLIError::IOError(path, error) => { 120 | eprintln!("io error {:?} with path '{}'", error, path) 121 | } 122 | CLIError::Clap(error) => eprintln!("{}", &error.message), 123 | CLIError::SerdeJSON(error) => eprintln!("json error {}", error), 124 | CLIError::PermissionDenied => { 125 | eprintln!("permission denied error! (are you running as sudo/admin)?") 126 | } 127 | CLIError::OtherMessage(msg) => eprintln!("error: {}", &msg), 128 | CLIError::Other(e) => eprintln!("error: {}", e), 129 | CLIError::HCIAdapterError(e) => eprintln!("hci adapter error: {}", e), 130 | }; 131 | std::process::exit(0); 132 | } 133 | () 134 | } 135 | -------------------------------------------------------------------------------- /docs/mesh_layout.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/docs/mesh_layout.PNG -------------------------------------------------------------------------------- /docs/mesh_net_incoming_flowchart.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/docs/mesh_net_incoming_flowchart.PNG -------------------------------------------------------------------------------- /docs/mesh_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/docs/mesh_stack.png -------------------------------------------------------------------------------- /src/access.rs: -------------------------------------------------------------------------------- 1 | //! Access Layer between Models and the rest of the stack (Transport, Network, etc). The most 2 | //! surface layer of the stack. 3 | use crate::bytes::ToFromBytesEndian; 4 | use crate::mesh::{CompanyID, ModelID}; 5 | 6 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 7 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 8 | pub struct SigModelID(u16); 9 | impl SigModelID { 10 | pub const fn byte_len() -> usize { 11 | 2 12 | } 13 | } 14 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 15 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 16 | pub struct VendorModelID(u16); 17 | impl VendorModelID { 18 | pub const fn byte_len() -> usize { 19 | CompanyID::byte_len() + 2 20 | } 21 | } 22 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 23 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 24 | pub enum SigOpcode { 25 | SingleOctet(u8), 26 | DoubleOctet(u16), 27 | } 28 | impl SigOpcode { 29 | pub fn byte_len(&self) -> usize { 30 | match self { 31 | SigOpcode::SingleOctet(_) => 1, 32 | SigOpcode::DoubleOctet(_) => 2, 33 | } 34 | } 35 | } 36 | impl From for Opcode { 37 | fn from(opcode: SigOpcode) -> Self { 38 | Opcode::SIG(opcode) 39 | } 40 | } 41 | const VENDOR_OPCODE_MAX: u8 = (1_u8 << 6) - 1; 42 | /// 6 bit Vendor Opcode 43 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 44 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 45 | pub struct VendorOpcode(u8); 46 | impl VendorOpcode { 47 | pub fn new(opcode: u8) -> Self { 48 | assert!(opcode <= VENDOR_OPCODE_MAX); 49 | VendorOpcode(opcode) 50 | } 51 | } 52 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 53 | pub struct OpcodeConversationError(pub ()); 54 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 55 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 56 | pub enum Opcode { 57 | SIG(SigOpcode), 58 | Vendor(VendorOpcode, CompanyID), 59 | } 60 | impl Opcode { 61 | pub fn company_id(&self) -> Option { 62 | match self { 63 | Opcode::Vendor(_, cid) => Some(*cid), 64 | _ => None, 65 | } 66 | } 67 | pub fn is_sig(&self) -> bool { 68 | self.company_id().is_none() 69 | } 70 | pub fn is_vendor(&self) -> bool { 71 | !self.is_sig() 72 | } 73 | pub fn byte_len(&self) -> usize { 74 | match self { 75 | Opcode::SIG(o) => o.byte_len(), 76 | Opcode::Vendor(_, _) => 3, 77 | } 78 | } 79 | pub const fn max_byte_len() -> usize { 80 | 3 81 | } 82 | pub fn unpack_from(bytes: &[u8]) -> Result { 83 | if bytes.is_empty() || bytes.len() > Self::max_byte_len() { 84 | Err(OpcodeConversationError(())) 85 | } else if bytes[0] == 0x7F { 86 | // This opcode is RFU 87 | Err(OpcodeConversationError(())) 88 | } else if bytes[0] & 0x80 == 0 { 89 | Ok(Opcode::SIG(SigOpcode::SingleOctet(bytes[0]).into())) 90 | } else if bytes[0] & 0xC0 == 0xC0 { 91 | if bytes.len() < 3 { 92 | return Err(OpcodeConversationError(())); 93 | } 94 | let vendor_opcode = VendorOpcode::new(bytes[0] & !0xC0); 95 | let company_id = CompanyID(u16::from_le_bytes([bytes[1], bytes[2]])); 96 | Ok(Opcode::Vendor(vendor_opcode, company_id)) 97 | } else if bytes[0] & 0x80 == 1 { 98 | if bytes.len() < 2 { 99 | return Err(OpcodeConversationError(())); 100 | } 101 | Ok(Opcode::SIG(SigOpcode::DoubleOctet(u16::from_le_bytes([ 102 | bytes[0], bytes[1], 103 | ])))) 104 | } else { 105 | Err(OpcodeConversationError(())) 106 | } 107 | } 108 | pub fn pack_into(&self, buffer: &mut [u8]) -> Result<(), OpcodeConversationError> { 109 | match *self { 110 | Opcode::SIG(s) => match s { 111 | SigOpcode::SingleOctet(s) => { 112 | if buffer.len() < 1 { 113 | return Err(OpcodeConversationError(())); 114 | } 115 | if s & 0x80 == 0 && s != 0x7F { 116 | buffer[0] = s; 117 | Ok(()) 118 | } else { 119 | Err(OpcodeConversationError(())) 120 | } 121 | } 122 | SigOpcode::DoubleOctet(d) => { 123 | if buffer.len() < 2 { 124 | return Err(OpcodeConversationError(())); 125 | } 126 | if d & 0xC000 == 0x8000 { 127 | buffer[..2].copy_from_slice(&d.to_le_bytes()[..]); 128 | Ok(()) 129 | } else { 130 | Err(OpcodeConversationError(())) 131 | } 132 | } 133 | }, 134 | Opcode::Vendor(opcode, company_id) => { 135 | if buffer.len() < 3 { 136 | return Err(OpcodeConversationError(())); 137 | } 138 | // Invalid 6-bit opcode 139 | if opcode.0 > !0xC0 { 140 | return Err(OpcodeConversationError(())); 141 | } 142 | buffer[0] = opcode.0 | 0xC0; 143 | buffer[1..3].copy_from_slice(&company_id.to_bytes_le()[..]); 144 | Ok(()) 145 | } 146 | } 147 | } 148 | } 149 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] 150 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 151 | pub struct ModelIdentifier { 152 | model_id: ModelID, 153 | company_id: Option, 154 | } 155 | impl ModelIdentifier { 156 | pub fn new_sig(sig_model_id: ModelID) -> ModelIdentifier { 157 | ModelIdentifier { 158 | model_id: sig_model_id, 159 | company_id: None, 160 | } 161 | } 162 | /// Creates a new vendor model from a `ModelID` and Bluetooth `CompanyID` 163 | pub fn new_vendor(model_id: ModelID, company_id: CompanyID) -> ModelIdentifier { 164 | ModelIdentifier { 165 | model_id, 166 | company_id: Some(company_id), 167 | } 168 | } 169 | /// Returns the byte length of a vendor `ModelIdentifier`. 170 | /// (`vendor_byte_len() == ModelID::byte_len() + CompanyID::byte_len()`) 171 | pub const fn vendor_byte_len() -> usize { 172 | ModelID::byte_len() + CompanyID::byte_len() 173 | } 174 | /// Returns the byte length of a SIG `ModelIdentifier`. 175 | /// (`sig_byte_len() == ModelID::byte_len()`) 176 | pub const fn sig_byte_len() -> usize { 177 | ModelID::byte_len() 178 | } 179 | pub fn byte_len(&self) -> usize { 180 | if self.is_vendor() { 181 | Self::vendor_byte_len() 182 | } else { 183 | Self::sig_byte_len() 184 | } 185 | } 186 | /// Returns the `ModelID` of the model. 187 | pub fn model_id(&self) -> ModelID { 188 | self.model_id 189 | } 190 | /// Returns the `CompanyID` of the vendor model or `None` if it's a SIG model. 191 | pub fn company_id(&self) -> Option { 192 | self.company_id 193 | } 194 | /// Returns if the `ModelIdentifier` is a SIG model. 195 | pub fn is_sig(&self) -> bool { 196 | self.company_id.is_none() 197 | } 198 | /// Returns if the `ModelIdentifier` is a vendor model. 199 | pub fn is_vendor(&self) -> bool { 200 | !self.is_sig() 201 | } 202 | /// Packs the `ModelIdentifier` into a little endian byte buffer. The `buf` must have enough 203 | /// room for the `ModelIdentifier`! (Usually 2 or 4 bytes). 204 | /// # Panics 205 | /// Panics if `buf.len() < self.byte_len()` 206 | pub fn pack_into(&self, buf: &mut [u8]) { 207 | assert!(buf.len() >= self.byte_len()); 208 | (match self.company_id { 209 | None => buf, 210 | Some(company_id) => { 211 | buf[..CompanyID::byte_len()].copy_from_slice(&company_id.to_bytes_le()); 212 | &mut buf[CompanyID::byte_len()..] 213 | } 214 | }[..VendorModelID::byte_len()]) 215 | .copy_from_slice(&self.model_id.to_bytes_le()); 216 | } 217 | pub fn unpack_from(buf: &[u8]) -> Option { 218 | match buf.len() { 219 | 4 => Some(Self::new_sig(ModelID::from_bytes_le(buf)?)), 220 | 8 => Some(Self::new_vendor( 221 | ModelID::from_bytes_le(&buf[2..4])?, 222 | CompanyID::from_bytes_le(&buf[..2])?, 223 | )), 224 | _ => None, 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/advertisement.rs: -------------------------------------------------------------------------------- 1 | use crate::bearer::{BearerError, IncomingEncryptedNetworkPDU, OutgoingEncryptedNetworkPDU}; 2 | use crate::btle::advertisement::{AdStructure, RawAdvertisement}; 3 | use crate::btle::advertiser::{Advertiser, Scanner, ScannerSink}; 4 | use crate::interface::{InputInterface, InterfaceSink, OutputInterface}; 5 | use crate::net; 6 | 7 | impl From> for btle::advertisement::AdStructure { 8 | fn from(pdu: net::EncryptedPDU<'_>) -> Self { 9 | AdStructure::MeshPDU(btle::advertisement::AdStructureDataBuffer::new(pdu.data())) 10 | } 11 | } 12 | 13 | pub struct ScannerInterface>> { 14 | scanner: Scan, 15 | _marker: core::marker::PhantomData, 16 | } 17 | pub struct ScannerInputSink(InterSink); 18 | impl Clone for ScannerInputSink { 19 | fn clone(&self) -> Self { 20 | Self(self.0.clone()) 21 | } 22 | } 23 | impl ScannerSink for ScannerInputSink { 24 | fn consume_advertisement(&mut self, advertisement: &RawAdvertisement) { 25 | match advertisement.iter().next() { 26 | Some(AdStructure::MeshPDU(buf)) => { 27 | if let Some(pdu) = net::OwnedEncryptedPDU::new(buf.as_ref()) { 28 | let incoming = IncomingEncryptedNetworkPDU { 29 | encrypted_pdu: pdu, 30 | rssi: advertisement.rssi(), 31 | dont_relay: false, 32 | }; 33 | self.0.consume_pdu(&incoming); 34 | } 35 | } 36 | _ => (), 37 | } 38 | } 39 | } 40 | 41 | impl OutputInterface for A { 42 | fn send_pdu(&mut self, pdu: &OutgoingEncryptedNetworkPDU) -> Result<(), BearerError> { 43 | for _ in 0..u8::from(pdu.transmit_parameters.count) { 44 | self.advertise(&(&AdStructure::from(pdu.pdu.as_ref())).into()) 45 | .map_err(|_| BearerError::AdvertiseError)?; 46 | } 47 | Ok(()) 48 | } 49 | } 50 | impl>> InputInterface 51 | for ScannerInterface 52 | { 53 | fn take_sink(&mut self, sink: InterSink) { 54 | self.scanner.take_sink(ScannerInputSink(sink)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/crypto/aes.rs: -------------------------------------------------------------------------------- 1 | //! A module for crypto AES functions. Essentially a wrapper around a 3rd party AES crypto lib 2 | //! (aes_soft in this case). This lets the rest of the library code to not have a hard dependence 3 | //! on any 3rd party libs. Bluetooth Mesh uses 128-bit exclusively as its Key bit size. 4 | 5 | use crate::bytes::ToFromBytesEndian; 6 | use crate::crypto::aes_ccm::AesCcm; 7 | use crate::crypto::key::Key; 8 | use crate::crypto::{nonce::Nonce, Salt, MIC}; 9 | use aes::Aes128; 10 | use block_modes::block_padding::ZeroPadding; 11 | use block_modes::cipher::NewBlockCipher; 12 | use block_modes::BlockMode; 13 | use cmac::{Cmac, FromBlockCipher, Mac}; 14 | use core::convert::TryInto; 15 | use core::slice; 16 | use generic_array::GenericArray; 17 | use typenum::consts::{U4, U8}; 18 | 19 | const AES_BLOCK_LEN: usize = 16; 20 | type AesBlock = [u8; AES_BLOCK_LEN]; 21 | const ZERO_BLOCK: AesBlock = [0_u8; AES_BLOCK_LEN]; 22 | /// Returned when a key can't be used to decrypt. (Wrong Key?) 23 | #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 24 | pub struct Error; 25 | type AesEcb = block_modes::Ecb; 26 | type AesCcmBigMic = AesCcm; 27 | type AesCcmSmallMic = AesCcm; 28 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug, Hash)] 29 | pub enum MicSize { 30 | Big, 31 | Small, 32 | } 33 | impl MicSize { 34 | pub fn byte_size(self) -> usize { 35 | match self { 36 | MicSize::Big => MIC::big_size(), 37 | MicSize::Small => MIC::small_size(), 38 | } 39 | } 40 | pub fn is_big(self) -> bool { 41 | match self { 42 | MicSize::Big => true, 43 | MicSize::Small => false, 44 | } 45 | } 46 | } 47 | pub struct AESCipher(Aes128); 48 | impl AESCipher { 49 | #[must_use] 50 | pub fn new(key: &Key) -> AESCipher { 51 | AESCipher(Aes128::new(GenericArray::from_slice(key.as_ref()))) 52 | } 53 | #[must_use] 54 | fn cipher(&self) -> &Aes128 { 55 | &self.0 56 | } 57 | #[must_use] 58 | fn ecb_cipher(&self) -> AesEcb { 59 | AesEcb::new(self.cipher().clone(), &Default::default()) 60 | } 61 | #[must_use] 62 | fn cmac_cipher(&self) -> Cmac { 63 | Cmac::from_cipher(self.cipher().clone()) 64 | } 65 | #[must_use] 66 | fn ccm_big_mic_cipher(&self) -> AesCcmBigMic { 67 | self.cipher().into() 68 | } 69 | #[must_use] 70 | fn ccm_small_mic_cipher(&self) -> AesCcmSmallMic { 71 | self.cipher().into() 72 | } 73 | /// Decrypts `input` in-place with 128-bit `key` back into `input`. 74 | #[must_use] 75 | pub fn ecb_decrypt(&self, input: &mut [u8]) -> Result<(), Error> { 76 | self.ecb_cipher().decrypt(input).or(Err(Error))?; 77 | Ok(()) 78 | } 79 | /// Encrypt `input` in-place with 128-bit `key` back into `input`. 80 | pub fn ecb_encrypt(&self, input: &mut [u8]) { 81 | let input_len = input.len(); 82 | let mut ecb_cipher = self.ecb_cipher(); 83 | // ecb encrypt input len must be a multiple of 16 but `input` might not be. 84 | // So we chunk the input and we encrypt all the blocks in place expect the last block gets copy. 85 | { 86 | let chunks = input.chunks_exact_mut(AES_BLOCK_LEN); 87 | for block_u8s in chunks { 88 | let block_ga = GenericArray::from_mut_slice(block_u8s); 89 | ecb_cipher.encrypt_blocks(slice::from_mut(block_ga)); 90 | } 91 | } 92 | // Recalculate length aligned to block size. Integer division is used to align the len. 93 | let aligned_len = (input_len / AES_BLOCK_LEN) * AES_BLOCK_LEN; 94 | let rest = &mut input[..aligned_len]; 95 | // If `input.len()` is not evenly divide into blocks (16 bytes), encrypt the last bit of 96 | // data not in place. 97 | if !rest.is_empty() { 98 | let l = rest.len(); 99 | let mut block_buf = ZERO_BLOCK; 100 | block_buf[..l].copy_from_slice(rest); 101 | ecb_cipher.encrypt_blocks(slice::from_mut(GenericArray::from_mut_slice( 102 | &mut block_buf[..], 103 | ))); 104 | rest.copy_from_slice(&block_buf[..l]); 105 | } 106 | } 107 | #[must_use] 108 | pub fn cmac(&self, m: &[u8]) -> Key { 109 | self.cmac_slice(&[m]) 110 | } 111 | #[must_use] 112 | pub fn cmac_slice(&self, ms: &[&[u8]]) -> Key { 113 | let mut cmac_context = self.cmac_cipher(); 114 | for m in ms { 115 | if !m.is_empty() { 116 | cmac_context.update(m); 117 | } 118 | } 119 | AsRef::<[u8]>::as_ref(&cmac_context.finalize().into_bytes()) 120 | .try_into() 121 | .expect("cmac code should be 16 bytes (SALT_LEN)") 122 | } 123 | pub fn ccm_encrypt( 124 | &self, 125 | nonce: &Nonce, 126 | associated_data: &[u8], 127 | payload: &mut [u8], 128 | mic_size: MicSize, 129 | ) -> MIC { 130 | let nonce = nonce.as_ref().into(); 131 | match mic_size { 132 | MicSize::Big => self 133 | .ccm_big_mic_cipher() 134 | .encrypt(nonce, associated_data, payload) 135 | .expect("payload or associated data too big") 136 | .as_slice() 137 | .try_into() 138 | .unwrap(), 139 | MicSize::Small => self 140 | .ccm_small_mic_cipher() 141 | .encrypt(nonce, associated_data, payload) 142 | .expect("payload or associated data too big") 143 | .as_slice() 144 | .try_into() 145 | .unwrap(), 146 | } 147 | } 148 | /// AES CCM decryption of the payload. To supply no associated data, pass it an empty slice 149 | /// (such as `b""`). This function will return an [`Error`] 150 | pub fn ccm_decrypt( 151 | &self, 152 | nonce: &Nonce, 153 | associated_data: &[u8], 154 | payload: &mut [u8], 155 | mic: MIC, 156 | ) -> Result<(), Error> { 157 | let nonce = nonce.as_ref().into(); 158 | match mic { 159 | MIC::Big(b) => self 160 | .ccm_small_mic_cipher() 161 | .decrypt( 162 | nonce, 163 | associated_data, 164 | payload, 165 | b.to_bytes_be().as_ref().into(), 166 | ) 167 | .or(Err(Error)), 168 | MIC::Small(s) => self 169 | .ccm_small_mic_cipher() 170 | .decrypt( 171 | nonce, 172 | associated_data, 173 | payload, 174 | s.to_bytes_be().as_ref().into(), 175 | ) 176 | .or(Err(Error)), 177 | } 178 | } 179 | } 180 | 181 | impl From for AESCipher { 182 | fn from(k: Key) -> Self { 183 | Self::new(&k) 184 | } 185 | } 186 | impl From<&'_ Key> for AESCipher { 187 | fn from(k: &Key) -> Self { 188 | Self::new(k) 189 | } 190 | } 191 | impl From for AESCipher { 192 | fn from(s: Salt) -> Self { 193 | s.as_key().into() 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/crypto/aes_cmac.rs: -------------------------------------------------------------------------------- 1 | 2 | use dbl::Dbl; 3 | 4 | use aes::block_cipher_trait::generic_array::{typenum::Unsigned, ArrayLength, GenericArray}; 5 | use aes::block_cipher_trait::BlockCipher; 6 | use core::fmt; 7 | //! AES CMAC module. Based on `aes_cmac` crate but had to be reimplemented because it was missing 8 | //! certain public functions. 9 | // Copied from cmac crates because they didn't make `from_cipher` public :( 10 | 11 | #[derive(Clone)] 12 | pub struct MacResult> { 13 | code: GenericArray, 14 | } 15 | impl> MacResult { 16 | pub fn code(self) -> GenericArray { 17 | self.code 18 | } 19 | } 20 | #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] 21 | pub struct MacError; 22 | 23 | #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] 24 | pub struct InvalidKeyLength; 25 | 26 | 27 | type Block = GenericArray; 28 | 29 | /// Generic CMAC instance 30 | #[derive(Clone)] 31 | pub struct Cmac 32 | where 33 | C: BlockCipher + Clone, 34 | Block: Dbl, 35 | { 36 | cipher: C, 37 | key1: Block, 38 | key2: Block, 39 | buffer: Block, 40 | pos: usize, 41 | } 42 | 43 | impl Cmac 44 | where 45 | C: BlockCipher + Clone, 46 | Block: Dbl, 47 | { 48 | pub fn from_cipher(cipher: C) -> Self { 49 | let mut subkey = GenericArray::default(); 50 | cipher.encrypt_block(&mut subkey); 51 | 52 | let key1 = subkey.dbl(); 53 | let key2 = key1.clone().dbl(); 54 | 55 | Cmac { 56 | cipher, 57 | key1, 58 | key2, 59 | buffer: Default::default(), 60 | pos: 0, 61 | } 62 | } 63 | } 64 | 65 | #[inline(always)] 66 | fn xor>(buf: &mut Block, data: &Block) { 67 | for i in 0..L::to_usize() { 68 | buf[i] ^= data[i]; 69 | } 70 | } 71 | 72 | impl Cmac 73 | where 74 | C: BlockCipher + Clone, 75 | Block: Dbl, 76 | C::BlockSize: Clone, 77 | { 78 | pub fn new(key: &GenericArray) -> Self { 79 | Self::from_cipher(C::new(key)) 80 | } 81 | 82 | pub fn new_varkey(key: &[u8]) -> Result { 83 | let cipher = C::new_varkey(key).map_err(|_| InvalidKeyLength)?; 84 | Ok(Self::from_cipher(cipher)) 85 | } 86 | 87 | #[inline] 88 | pub fn input(&mut self, mut data: &[u8]) { 89 | let n = C::BlockSize::to_usize(); 90 | 91 | let rem = n - self.pos; 92 | if data.len() >= rem { 93 | let (l, r) = data.split_at(rem); 94 | data = r; 95 | for (a, b) in self.buffer[self.pos..].iter_mut().zip(l) { 96 | *a ^= *b; 97 | } 98 | self.pos = n; 99 | } else { 100 | for (a, b) in self.buffer[self.pos..].iter_mut().zip(data) { 101 | *a ^= *b; 102 | } 103 | self.pos += data.len(); 104 | return; 105 | } 106 | 107 | while data.len() >= n { 108 | self.cipher.encrypt_block(&mut self.buffer); 109 | 110 | let (l, r) = data.split_at(n); 111 | let block = unsafe { &*(l.as_ptr() as *const Block) }; 112 | data = r; 113 | 114 | xor(&mut self.buffer, block); 115 | } 116 | 117 | if data.len() != 0 { 118 | self.cipher.encrypt_block(&mut self.buffer); 119 | for (a, b) in self.buffer.iter_mut().zip(data) { 120 | *a ^= *b; 121 | } 122 | self.pos = data.len(); 123 | } 124 | } 125 | 126 | pub fn reset(&mut self) { 127 | self.buffer = Default::default(); 128 | self.pos = 0; 129 | } 130 | 131 | #[inline] 132 | pub fn result(mut self) -> MacResult { 133 | let n = C::BlockSize::to_usize(); 134 | let mut buf = self.buffer.clone(); 135 | if self.pos == n { 136 | xor(&mut buf, &self.key1); 137 | } else { 138 | xor(&mut buf, &self.key2); 139 | buf[self.pos] ^= 0x80; 140 | } 141 | self.cipher.encrypt_block(&mut buf); 142 | 143 | self.reset(); 144 | 145 | MacResult { code: buf } 146 | } 147 | } 148 | 149 | impl fmt::Debug for Cmac 150 | where 151 | C: BlockCipher + fmt::Debug + Clone, 152 | Block: Dbl, 153 | { 154 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 155 | write!(f, "Cmac-{:?}", self.cipher) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/crypto/ecdh.rs: -------------------------------------------------------------------------------- 1 | use crate::provisioning::protocol::PublicKey; 2 | use std::convert::TryInto; 3 | 4 | #[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Debug, Hash)] 5 | pub enum Error { 6 | KeyGenerationProblem, 7 | EarlyPublicKeyAgreementKey, 8 | Unspecfied, 9 | } 10 | 11 | #[derive(Clone)] 12 | pub struct DerivedPublicKey { 13 | key: ring::agreement::PublicKey, 14 | } 15 | impl DerivedPublicKey {} 16 | impl AsRef<[u8]> for DerivedPublicKey { 17 | fn as_ref(&self) -> &[u8] { 18 | &self.key.as_ref()[1..] 19 | } 20 | } 21 | impl From<&DerivedPublicKey> for PublicKey { 22 | fn from(k: &DerivedPublicKey) -> Self { 23 | let b = k.as_ref(); 24 | assert_eq!(64, b.len(), "derived public key wrong length"); 25 | PublicKey { 26 | x: (&b[..32]).try_into().expect("length checked above"), 27 | y: (&b[32..64]).try_into().expect("length checked above"), 28 | } 29 | } 30 | } 31 | pub struct PrivateKey { 32 | key: ring::agreement::EphemeralPrivateKey, 33 | } 34 | impl PrivateKey { 35 | pub fn new() -> Result { 36 | // ring is annoying and only allows `SystemRandom` which makes it hard to support 37 | // bare-metal environments so this will need to change in the future. 38 | Ok(PrivateKey { 39 | key: ring::agreement::EphemeralPrivateKey::generate( 40 | &ring::agreement::ECDH_P256, 41 | &ring::rand::SystemRandom::new(), 42 | ) 43 | .map_err(|_| Error::KeyGenerationProblem)?, 44 | }) 45 | } 46 | pub fn public_key(&self) -> Result { 47 | Ok(DerivedPublicKey { 48 | key: self 49 | .key 50 | .compute_public_key() 51 | .map_err(|_| Error::KeyGenerationProblem)?, 52 | }) 53 | } 54 | pub fn agree D>( 55 | self, 56 | public_key: &PublicKey, 57 | kdf: F, 58 | ) -> Result { 59 | const ELEM_LEN: usize = 32; 60 | let mut p_key = [0_u8; ELEM_LEN * 2 + 1]; 61 | p_key[0] = 0x04; 62 | p_key[1..1 + ELEM_LEN].copy_from_slice(public_key.x.as_ref()); 63 | p_key[1 + ELEM_LEN..].copy_from_slice(public_key.y.as_ref()); 64 | ring::agreement::agree_ephemeral( 65 | self.key, 66 | &ring::agreement::UnparsedPublicKey::new(&ring::agreement::ECDH_P256, p_key.as_ref()), 67 | kdf 68 | ).map_err(|_| Error::EarlyPublicKeyAgreementKey) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/crypto/k_funcs.rs: -------------------------------------------------------------------------------- 1 | //! Bluetooth Mesh Crypto Functions 2 | //! Defined in the Core Spec v1.0 3 | //! 4 | 5 | use crate::crypto::aes::AESCipher; 6 | use crate::crypto::key::{AppKey, EncryptionKey, Key, PrivacyKey, ZERO_KEY}; 7 | use crate::crypto::{Salt, AID}; 8 | use crate::mesh::NID; 9 | use core::convert::TryInto; 10 | 11 | /// k1 function from Mesh Core v1.0. N==`bytes` and P==`extra`. 12 | #[must_use] 13 | pub fn k1(n: &[u8], salt: &Salt, extra: &[u8]) -> Key { 14 | let t = AESCipher::from(*salt).cmac(n); 15 | AESCipher::from(t).cmac(extra) 16 | } 17 | #[must_use] 18 | pub fn k2(key: &Key, p: impl AsRef<[u8]>) -> (NID, EncryptionKey, PrivacyKey) { 19 | k2_bytes(key, p.as_ref()) 20 | } 21 | #[must_use] 22 | pub fn k2_bytes(n: &Key, p: &[u8]) -> (NID, EncryptionKey, PrivacyKey) { 23 | assert!(!p.is_empty(), "p must have at least one byte"); 24 | let t = AESCipher::from(SMK2).cmac(n.as_ref()); 25 | let cipher = AESCipher::from(t); 26 | let t_1 = cipher.cmac_slice(&[p, &[0x01]]); 27 | let t_2 = cipher.cmac_slice(&[t_1.as_ref(), p, &[0x02]]); 28 | let t_3 = cipher.cmac_slice(&[t_2.as_ref(), p, &[0x03]]); 29 | 30 | ( 31 | NID::new(t_1.as_ref()[15] & 0x7F), 32 | EncryptionKey::new(t_2), 33 | PrivacyKey::new(t_3), 34 | ) 35 | } 36 | #[must_use] 37 | pub fn k3(key: &Key) -> u64 { 38 | let t = AESCipher::from(SMK3).cmac(key.as_ref()); 39 | u64::from_be_bytes( 40 | AESCipher::from(t).cmac(b"id64\x01".as_ref()).as_ref()[8..] 41 | .try_into() 42 | .expect("aes_cmac returns 16 bytes and the last 8 get turned into a u64"), 43 | ) 44 | } 45 | #[must_use] 46 | pub fn k4(key: &AppKey) -> AID { 47 | let t = AESCipher::from(SMK4).cmac(key.as_ref().as_ref()); 48 | AID(AESCipher::from(t).cmac(b"id6\x01").as_ref()[15] & 0x3F) 49 | } 50 | 51 | /// Calculates Bluetooth Mesh's `s1` on bytes. Common values are precomputed and 52 | /// hardcore to avoid recalculating `s1` unneededly. 53 | #[must_use] 54 | pub fn s1(m: impl AsRef<[u8]>) -> Salt { 55 | s1_bytes(m.as_ref()) 56 | } 57 | /// `VTAD == s1("vtad")` 58 | pub const VTAD: Salt = Salt([ 59 | 0xce, 0xf7, 0xfa, 0x9d, 0xc4, 0x7b, 0xaf, 0x5d, 0xaa, 0xee, 0xd1, 0x94, 0x6, 0x9, 0x4f, 0x37, 60 | ]); 61 | /// `PRDK == s1("prdk")` 62 | pub const PRDK: Salt = Salt([ 63 | 234, 68, 121, 239, 105, 21, 232, 251, 128, 106, 70, 188, 231, 231, 229, 72, 64 | ]); 65 | /// `SMK1 == s1("smk1")` 66 | pub const SMK1: Salt = Salt([ 67 | 0xaa, 0x20, 0x18, 0xc6, 0x98, 0xe8, 0xb2, 0xef, 0x77, 0x75, 0x37, 0x19, 0xe9, 0xf1, 0xa8, 0x4, 68 | ]); 69 | /// `SMK2 == s1("smk2")` 70 | pub const SMK2: Salt = Salt([ 71 | 0x4f, 0x90, 0x48, 0xc, 0x18, 0x71, 0xbf, 0xbf, 0xfd, 0x16, 0x97, 0x1f, 0x4d, 0x8d, 0x10, 0xb1, 72 | ]); 73 | 74 | /// `SMK3 == s1("smk3")` 75 | pub const SMK3: Salt = Salt([ 76 | 0x0, 0x36, 0x44, 0x35, 0x3, 0xf1, 0x95, 0xcc, 0x8a, 0x71, 0x6e, 0x13, 0x62, 0x91, 0xc3, 0x2, 77 | ]); 78 | /// `SMK4 == s1("smk4")` 79 | pub const SMK4: Salt = Salt([ 80 | 0xe, 0x9a, 0xc1, 0xb7, 0xce, 0xfa, 0x66, 0x87, 0x4c, 0x97, 0xee, 0x54, 0xac, 0x5f, 0x49, 0xbe, 81 | ]); 82 | #[must_use] 83 | pub fn s1_bytes(m: &[u8]) -> Salt { 84 | AESCipher::new(&ZERO_KEY).cmac(m).as_salt() 85 | } 86 | #[must_use] 87 | pub fn s1_slice(m: &[&[u8]]) -> Salt { 88 | AESCipher::new(&ZERO_KEY).cmac_slice(m).as_salt() 89 | } 90 | #[must_use] 91 | pub fn id128(n: &Key, s: impl AsRef<[u8]>) -> Key { 92 | id128_bytes(n, s.as_ref()) 93 | } 94 | #[must_use] 95 | pub fn id128_bytes(n: &Key, s: &[u8]) -> Key { 96 | let salt = s1_bytes(s); 97 | k1(n.as_ref(), &salt, b"id128\x01") 98 | } 99 | 100 | /// Tests based on Mesh Core v1.0 Sample Data. 101 | #[cfg(test)] 102 | mod tests { 103 | use super::*; 104 | use crate::crypto::hex_16_to_array; 105 | use crate::crypto::key::NetKey; 106 | 107 | fn sample_net_key() -> NetKey { 108 | NetKey::from_hex("f7a2a44f8e8a8029064f173ddc1e2b00").unwrap() 109 | } 110 | 111 | #[test] 112 | fn test_s1() { 113 | assert_eq!( 114 | s1("test"), 115 | Salt::from_hex("b73cefbd641ef2ea598c2b6efb62f79c").unwrap() 116 | ); 117 | } 118 | 119 | #[test] 120 | fn test_s1_precomputed() { 121 | assert_eq!(s1("prkd"), PRDK); 122 | assert_eq!(s1("vtad"), VTAD); 123 | assert_eq!(s1("smk1"), SMK1); 124 | assert_eq!(s1("smk2"), SMK2); 125 | assert_eq!(s1("smk3"), SMK3); 126 | assert_eq!(s1("smk4"), SMK4); 127 | } 128 | 129 | #[test] 130 | fn test_k1() { 131 | let key = Key::from_hex("3216d1509884b533248541792b877f98").unwrap(); 132 | let salt = Salt::from_hex("2ba14ffa0df84a2831938d57d276cab4").unwrap(); 133 | let p = hex_16_to_array("5a09d60797eeb4478aada59db3352a0d").unwrap(); 134 | let expected = Key::from_hex("f6ed15a8934afbe7d83e8dcb57fcf5d7").unwrap(); 135 | assert_eq!(k1(key.as_ref(), &salt, &p[..]), expected); 136 | } 137 | 138 | #[test] 139 | fn test_k2_friendship() { 140 | let nid = NID::new(0x7F); 141 | let encryption_key = EncryptionKey::from_hex("9f589181a0f50de73c8070c7a6d27f46").unwrap(); 142 | let privacy_key = PrivacyKey::from_hex("4c715bd4a64b938f99b453351653124f").unwrap(); 143 | assert_eq!( 144 | k2(&sample_net_key().key(), b"\x00"), 145 | (nid, encryption_key, privacy_key) 146 | ); 147 | } 148 | #[test] 149 | fn test_k2_master() { 150 | let nid = NID::new(0x73); 151 | let encryption_key = EncryptionKey::from_hex("11efec0642774992510fb5929646df49").unwrap(); 152 | let privacy_key = PrivacyKey::from_hex("d4d7cc0dfa772d836a8df9df5510d7a7").unwrap(); 153 | assert_eq!( 154 | k2( 155 | &sample_net_key().key(), 156 | b"\x01\x02\x03\x04\x05\x06\x07\x08\x09" 157 | ), 158 | (nid, encryption_key, privacy_key) 159 | ); 160 | } 161 | 162 | #[test] 163 | fn test_k3() { 164 | let key = Key::from_hex("f7a2a44f8e8a8029064f173ddc1e2b00").unwrap(); 165 | assert_eq!(0xff046958233db014u64, k3(&key)); 166 | } 167 | #[test] 168 | fn test_k4() { 169 | let app_key = AppKey::from_hex("3216d1509884b533248541792b877f98").unwrap(); 170 | assert_eq!(AID(0x38), k4(&app_key)) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/crypto/nonce.rs: -------------------------------------------------------------------------------- 1 | //! Bluetooth Mesh nonces. Based on Mesh Core Spec v1.0. 2 | use crate::address::{Address, UnicastAddress}; 3 | use crate::bytes::ToFromBytesEndian; 4 | use crate::crypto::{k1, ECDHSecret, ProvisioningSalt}; 5 | use crate::mesh::{IVIndex, SequenceNumber, CTL, TTL}; 6 | use std::convert::TryInto; 7 | 8 | const NONCE_LEN: usize = 13; 9 | const ZERO_NONCE_BYTES: [u8; NONCE_LEN] = [0_u8; NONCE_LEN]; 10 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 11 | pub struct Nonce([u8; NONCE_LEN]); 12 | impl Nonce { 13 | pub fn new(bytes: [u8; NONCE_LEN]) -> Nonce { 14 | Nonce(bytes) 15 | } 16 | } 17 | impl AsRef<[u8]> for Nonce { 18 | fn as_ref(&self) -> &[u8] { 19 | &self.0[..] 20 | } 21 | } 22 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 23 | pub struct NetworkNonce(Nonce); 24 | impl NetworkNonce { 25 | pub fn new(nonce: Nonce) -> Self { 26 | Self(nonce) 27 | } 28 | pub fn new_bytes(bytes: [u8; NONCE_LEN]) -> Self { 29 | Self(Nonce(bytes)) 30 | } 31 | } 32 | impl AsRef<[u8]> for NetworkNonce { 33 | fn as_ref(&self) -> &[u8] { 34 | self.0.as_ref() 35 | } 36 | } 37 | impl AsRef for NetworkNonce { 38 | fn as_ref(&self) -> &Nonce { 39 | &self.0 40 | } 41 | } 42 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 43 | pub struct AppNonce(Nonce); 44 | impl AppNonce { 45 | pub fn new(nonce: Nonce) -> Self { 46 | Self(nonce) 47 | } 48 | pub fn new_bytes(bytes: [u8; NONCE_LEN]) -> Self { 49 | Self(Nonce(bytes)) 50 | } 51 | } 52 | impl AsRef<[u8]> for AppNonce { 53 | fn as_ref(&self) -> &[u8] { 54 | self.0.as_ref() 55 | } 56 | } 57 | impl AsRef for AppNonce { 58 | fn as_ref(&self) -> &Nonce { 59 | &self.0 60 | } 61 | } 62 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 63 | pub struct DeviceNonce(Nonce); 64 | impl DeviceNonce { 65 | pub fn new(nonce: Nonce) -> Self { 66 | Self(nonce) 67 | } 68 | pub fn new_bytes(bytes: [u8; NONCE_LEN]) -> Self { 69 | Self(Nonce(bytes)) 70 | } 71 | } 72 | impl AsRef<[u8]> for DeviceNonce { 73 | fn as_ref(&self) -> &[u8] { 74 | self.0.as_ref() 75 | } 76 | } 77 | impl AsRef for DeviceNonce { 78 | fn as_ref(&self) -> &Nonce { 79 | &self.0 80 | } 81 | } 82 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 83 | pub struct ProxyNonce(Nonce); 84 | impl ProxyNonce { 85 | pub fn new(nonce: Nonce) -> Self { 86 | Self(nonce) 87 | } 88 | pub fn new_bytes(bytes: [u8; NONCE_LEN]) -> Self { 89 | Self(Nonce(bytes)) 90 | } 91 | } 92 | impl AsRef for ProxyNonce { 93 | fn as_ref(&self) -> &Nonce { 94 | &self.0 95 | } 96 | } 97 | /// Nonce Types 98 | /// 0x04--0xFF RFU 99 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 100 | #[repr(u8)] 101 | pub enum NonceType { 102 | Network = 0x00, 103 | Application = 0x01, 104 | Device = 0x02, 105 | Proxy = 0x03, 106 | } 107 | impl NonceType { 108 | pub fn as_u8(self) -> u8 { 109 | self as u8 110 | } 111 | } 112 | 113 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] 114 | pub struct NetworkNonceParts { 115 | ctl: CTL, 116 | ttl: TTL, 117 | src: UnicastAddress, 118 | seq: SequenceNumber, 119 | iv_index: IVIndex, 120 | } 121 | 122 | impl NetworkNonceParts { 123 | #[must_use] 124 | pub fn new( 125 | ctl: CTL, 126 | ttl: TTL, 127 | src: UnicastAddress, 128 | seq: SequenceNumber, 129 | iv_index: IVIndex, 130 | ) -> Self { 131 | Self { 132 | ctl, 133 | ttl, 134 | src, 135 | seq, 136 | iv_index, 137 | } 138 | } 139 | #[must_use] 140 | pub fn to_nonce(&self) -> NetworkNonce { 141 | let seq = self.seq.to_bytes_be(); 142 | let src = self.src.to_bytes_be(); 143 | let iv = self.iv_index.to_bytes_be(); 144 | NetworkNonce::new_bytes([ 145 | NonceType::Network.as_u8(), 146 | self.ttl.with_flag(self.ctl.0), 147 | seq[2], 148 | seq[1], 149 | seq[0], 150 | src[1], 151 | src[0], 152 | 0x00, 153 | 0x00, 154 | iv[3], 155 | iv[2], 156 | iv[1], 157 | iv[0], 158 | ]) 159 | } 160 | } 161 | 162 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] 163 | pub struct AppNonceParts { 164 | pub aszmic: bool, 165 | pub seq: SequenceNumber, 166 | pub src: UnicastAddress, 167 | pub dst: Address, 168 | pub iv_index: IVIndex, 169 | } 170 | 171 | impl AppNonceParts { 172 | pub fn to_nonce(&self) -> AppNonce { 173 | let seq = self.seq.to_bytes_be(); 174 | let src = self.src.to_bytes_be(); 175 | let dst = self.dst.to_bytes_be(); 176 | let iv = self.iv_index.to_bytes_be(); 177 | AppNonce::new_bytes([ 178 | NonceType::Application.as_u8(), 179 | (self.aszmic as u8) << 7, 180 | seq[0], 181 | seq[1], 182 | seq[2], 183 | src[0], 184 | src[1], 185 | dst[0], 186 | dst[1], 187 | iv[0], 188 | iv[1], 189 | iv[2], 190 | iv[3], 191 | ]) 192 | } 193 | } 194 | 195 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] 196 | pub struct DeviceNonceParts { 197 | pub aszmic: bool, 198 | pub seq: SequenceNumber, 199 | pub src: UnicastAddress, 200 | pub dst: Address, 201 | pub iv_index: IVIndex, 202 | } 203 | 204 | impl DeviceNonceParts { 205 | pub fn to_nonce(&self) -> DeviceNonce { 206 | let seq = self.seq.to_bytes_be(); 207 | let src = self.src.to_bytes_be(); 208 | let dst = self.src.to_bytes_be(); 209 | let iv = self.iv_index.to_bytes_be(); 210 | DeviceNonce::new_bytes([ 211 | NonceType::Device.as_u8(), 212 | (self.aszmic as u8) << 7, 213 | seq[2], 214 | seq[1], 215 | seq[0], 216 | src[1], 217 | src[0], 218 | dst[1], 219 | dst[0], 220 | iv[3], 221 | iv[2], 222 | iv[1], 223 | iv[0], 224 | ]) 225 | } 226 | } 227 | 228 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] 229 | pub struct ProxyNonceParts { 230 | seq: SequenceNumber, 231 | src: UnicastAddress, 232 | iv_index: IVIndex, 233 | } 234 | 235 | impl ProxyNonceParts { 236 | pub fn to_nonce(&self) -> ProxyNonce { 237 | let seq = self.seq.to_bytes_be(); 238 | let src = self.src.to_bytes_be(); 239 | let iv = self.iv_index.to_bytes_be(); 240 | ProxyNonce::new_bytes([ 241 | NonceType::Proxy.as_u8(), 242 | 0x00, 243 | seq[2], 244 | seq[1], 245 | seq[0], 246 | src[1], 247 | src[0], 248 | 0x00, 249 | 0x00, 250 | iv[3], 251 | iv[2], 252 | iv[1], 253 | iv[0], 254 | ]) 255 | } 256 | } 257 | 258 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 259 | pub struct SessionNonce(pub Nonce); 260 | impl SessionNonce { 261 | pub fn from_secret_salt(secret: &ECDHSecret, salt: &ProvisioningSalt) -> SessionNonce { 262 | SessionNonce(Nonce::new( 263 | (&k1(secret.as_ref(), salt.as_ref(), b"prsn").as_ref()[..NONCE_LEN]) 264 | .try_into() 265 | .expect("hard coded length"), 266 | )) 267 | } 268 | } 269 | impl AsRef for SessionNonce { 270 | fn as_ref(&self) -> &Nonce { 271 | &self.0 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/foundation/health.rs: -------------------------------------------------------------------------------- 1 | /// FaultID. According to Bluetooth Mesh Spec v1.0. Odd values are usually Warnings while even 2 | /// values are Errors. 3 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 4 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 5 | pub enum FaultID { 6 | NoFault, 7 | BatteryLowWarning, 8 | BatterLowError, 9 | SupplyVoltageTooLowWarning, 10 | SupplyVoltageTooLowError, 11 | SupplyVoltageTooHighWarning, 12 | SupplyVoltageTooHighError, 13 | PowerSupplyInterruptedWarning, 14 | PowerSupplyInterruptedError, 15 | NoLoadWarning, 16 | NoLoadError, 17 | OverloadWarning, 18 | OverloadError, 19 | OverheatWarning, 20 | OverheatError, 21 | CondensationWarning, 22 | CondensationError, 23 | VibrationWarning, 24 | VibrationError, 25 | ConfigurationWarning, 26 | ConfigurationError, 27 | ElementNotCalibratedWarning, 28 | ElementNotCalibratedError, 29 | MemoryWarning, 30 | MemoryError, 31 | SelfTestWarning, 32 | SelfTestError, 33 | InputTooLowWarning, 34 | InputTooLowError, 35 | InputTooHighWarning, 36 | InputTooHighError, 37 | InputNoChangeWarning, 38 | InputNoChangeError, 39 | ActuatorBlockedWarning, 40 | ActuatorBlockedError, 41 | HousingOpenedWarning, 42 | HousingOpenedError, 43 | TamperWarning, 44 | TamperError, 45 | DeviceMovedWarning, 46 | DeviceMovedError, 47 | DeviceDroppedWarning, 48 | DeviceDroppedError, 49 | OverflowWarning, 50 | OverflowError, 51 | EmptyWarning, 52 | EmptyError, 53 | InternalBusWarning, 54 | InternalBusError, 55 | MechanismJammedWarning, 56 | MechanismJammedError, 57 | RFU(u8), 58 | Vendor(u8), 59 | } 60 | 61 | impl From for u8 { 62 | fn from(fault_id: FaultID) -> Self { 63 | match fault_id { 64 | FaultID::NoFault => 0x00, 65 | FaultID::BatteryLowWarning => 0x01, 66 | FaultID::BatterLowError => 0x02, 67 | FaultID::SupplyVoltageTooLowWarning => 0x03, 68 | FaultID::SupplyVoltageTooLowError => 0x04, 69 | FaultID::SupplyVoltageTooHighWarning => 0x05, 70 | FaultID::SupplyVoltageTooHighError => 0x06, 71 | FaultID::PowerSupplyInterruptedWarning => 0x07, 72 | FaultID::PowerSupplyInterruptedError => 0x08, 73 | FaultID::NoLoadWarning => 0x09, 74 | FaultID::NoLoadError => 0x0A, 75 | FaultID::OverloadWarning => 0x0B, 76 | FaultID::OverloadError => 0x0C, 77 | FaultID::OverheatWarning => 0x0D, 78 | FaultID::OverheatError => 0x0E, 79 | FaultID::CondensationWarning => 0x0F, 80 | FaultID::CondensationError => 0x10, 81 | FaultID::VibrationWarning => 0x11, 82 | FaultID::VibrationError => 0x12, 83 | FaultID::ConfigurationWarning => 0x13, 84 | FaultID::ConfigurationError => 0x14, 85 | FaultID::ElementNotCalibratedWarning => 0x15, 86 | FaultID::ElementNotCalibratedError => 0x16, 87 | FaultID::MemoryWarning => 0x17, 88 | FaultID::MemoryError => 0x18, 89 | FaultID::SelfTestWarning => 0x19, 90 | FaultID::SelfTestError => 0x1A, 91 | FaultID::InputTooLowWarning => 0x1B, 92 | FaultID::InputTooLowError => 0x1C, 93 | FaultID::InputTooHighWarning => 0x1D, 94 | FaultID::InputTooHighError => 0x1E, 95 | FaultID::InputNoChangeWarning => 0x1F, 96 | FaultID::InputNoChangeError => 0x20, 97 | FaultID::ActuatorBlockedWarning => 0x21, 98 | FaultID::ActuatorBlockedError => 0x22, 99 | FaultID::HousingOpenedWarning => 0x23, 100 | FaultID::HousingOpenedError => 0x24, 101 | FaultID::TamperWarning => 0x25, 102 | FaultID::TamperError => 0x26, 103 | FaultID::DeviceMovedWarning => 0x27, 104 | FaultID::DeviceMovedError => 0x28, 105 | FaultID::DeviceDroppedWarning => 0x29, 106 | FaultID::DeviceDroppedError => 0x2A, 107 | FaultID::OverflowWarning => 0x2B, 108 | FaultID::OverflowError => 0x2C, 109 | FaultID::EmptyWarning => 0x2D, 110 | FaultID::EmptyError => 0x2E, 111 | FaultID::InternalBusWarning => 0x2F, 112 | FaultID::InternalBusError => 0x30, 113 | FaultID::MechanismJammedWarning => 0x31, 114 | FaultID::MechanismJammedError => 0x32, 115 | FaultID::RFU(id) => id, 116 | FaultID::Vendor(id) => id, 117 | } 118 | } 119 | } 120 | impl From for FaultID { 121 | fn from(b: u8) -> Self { 122 | // Until IntellJ stops complaining that this is non-exhaustive, ignore 123 | // the unreachable pattern at the end. 124 | #[allow(unreachable_patterns)] 125 | match b { 126 | 0x00 => FaultID::NoFault, 127 | 0x01 => FaultID::BatteryLowWarning, 128 | 0x02 => FaultID::BatterLowError, 129 | 0x03 => FaultID::SupplyVoltageTooLowWarning, 130 | 0x04 => FaultID::SupplyVoltageTooLowError, 131 | 0x05 => FaultID::SupplyVoltageTooHighWarning, 132 | 0x06 => FaultID::SupplyVoltageTooHighError, 133 | 0x07 => FaultID::PowerSupplyInterruptedWarning, 134 | 0x08 => FaultID::PowerSupplyInterruptedError, 135 | 0x09 => FaultID::NoLoadWarning, 136 | 0x0A => FaultID::NoLoadError, 137 | 0x0B => FaultID::OverloadWarning, 138 | 0x0C => FaultID::OverloadError, 139 | 0x0D => FaultID::OverheatWarning, 140 | 0x0E => FaultID::OverheatError, 141 | 0x0F => FaultID::CondensationWarning, 142 | 0x10 => FaultID::CondensationError, 143 | 0x11 => FaultID::VibrationWarning, 144 | 0x12 => FaultID::VibrationError, 145 | 0x13 => FaultID::ConfigurationWarning, 146 | 0x14 => FaultID::ConfigurationError, 147 | 0x15 => FaultID::ElementNotCalibratedWarning, 148 | 0x16 => FaultID::ElementNotCalibratedError, 149 | 0x17 => FaultID::MemoryWarning, 150 | 0x18 => FaultID::MemoryError, 151 | 0x19 => FaultID::SelfTestWarning, 152 | 0x1A => FaultID::SelfTestError, 153 | 0x1B => FaultID::InputTooLowWarning, 154 | 0x1C => FaultID::InputTooLowError, 155 | 0x1D => FaultID::InputTooHighWarning, 156 | 0x1E => FaultID::InputTooHighError, 157 | 0x1F => FaultID::InputNoChangeWarning, 158 | 0x20 => FaultID::InputNoChangeError, 159 | 0x21 => FaultID::ActuatorBlockedWarning, 160 | 0x22 => FaultID::ActuatorBlockedError, 161 | 0x23 => FaultID::HousingOpenedWarning, 162 | 0x24 => FaultID::HousingOpenedError, 163 | 0x25 => FaultID::TamperWarning, 164 | 0x26 => FaultID::TamperError, 165 | 0x27 => FaultID::DeviceMovedWarning, 166 | 0x28 => FaultID::DeviceMovedError, 167 | 0x29 => FaultID::DeviceDroppedWarning, 168 | 0x2A => FaultID::DeviceDroppedError, 169 | 0x2B => FaultID::OverflowWarning, 170 | 0x2C => FaultID::OverflowError, 171 | 0x2D => FaultID::EmptyWarning, 172 | 0x2E => FaultID::EmptyError, 173 | 0x2F => FaultID::InternalBusWarning, 174 | 0x30 => FaultID::InternalBusError, 175 | 0x31 => FaultID::MechanismJammedWarning, 176 | 0x32 => FaultID::MechanismJammedError, 177 | 0x33..=0x7F => FaultID::RFU(b), 178 | 0x80..=0xFF => FaultID::Vendor(b), 179 | _ => unreachable!("all u8 values should be handled"), //IntellJ complains if I remove this line 180 | } 181 | } 182 | } 183 | 184 | #[cfg(test)] 185 | mod tests { 186 | use super::FaultID; 187 | /// Tests to make sure that the `From` trait is matching the `Into` trait. 188 | #[test] 189 | pub fn test_fault_id() { 190 | for i in 0..=0xFFu8 { 191 | let fault_id = FaultID::from(i); 192 | assert_eq!(u8::from(fault_id), i); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/foundation/mod.rs: -------------------------------------------------------------------------------- 1 | //! Foundation Layer. Handles Publication, Config, etc. 2 | 3 | use crate::bytes::ToFromBytesEndian; 4 | use crate::foundation::element::ElementsComposition; 5 | use crate::mesh::CompanyID; 6 | use crate::upper::AppPayload; 7 | use alloc::boxed::Box; 8 | use alloc::vec::Vec; 9 | use core::convert::TryFrom; 10 | 11 | pub mod element; 12 | pub mod health; 13 | pub mod model; 14 | pub mod publication; 15 | pub mod state; 16 | // LITTLE ENDIAN 17 | 18 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 19 | pub struct StatusCodeConversationError(()); 20 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 21 | #[repr(u8)] 22 | pub enum StatusCode { 23 | Ok = 0x00, 24 | } 25 | impl StatusCode { 26 | pub const fn byte_len() -> usize { 27 | 1 28 | } 29 | } 30 | impl From for u8 { 31 | fn from(code: StatusCode) -> Self { 32 | code as u8 33 | } 34 | } 35 | impl TryFrom for StatusCode { 36 | type Error = StatusCodeConversationError; 37 | 38 | fn try_from(_value: u8) -> Result { 39 | unimplemented!() 40 | } 41 | } 42 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 43 | pub struct FoundationStateError(()); 44 | 45 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 46 | pub struct ProductID(pub u16); 47 | impl ProductID { 48 | pub const fn byte_len() -> usize { 49 | 2 50 | } 51 | } 52 | 53 | impl ToFromBytesEndian for ProductID { 54 | type AsBytesType = [u8; 2]; 55 | 56 | #[must_use] 57 | fn to_bytes_le(&self) -> Self::AsBytesType { 58 | (self.0).to_bytes_le() 59 | } 60 | 61 | #[must_use] 62 | fn to_bytes_be(&self) -> Self::AsBytesType { 63 | (self.0).to_bytes_be() 64 | } 65 | 66 | #[must_use] 67 | fn from_bytes_le(bytes: &[u8]) -> Option { 68 | Some(ProductID(u16::from_bytes_le(bytes)?)) 69 | } 70 | 71 | #[must_use] 72 | fn from_bytes_be(bytes: &[u8]) -> Option { 73 | Some(ProductID(u16::from_bytes_be(bytes)?)) 74 | } 75 | } 76 | 77 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 78 | pub struct VersionID(pub u16); 79 | impl VersionID { 80 | pub const fn byte_len() -> usize { 81 | 2 82 | } 83 | } 84 | impl ToFromBytesEndian for VersionID { 85 | type AsBytesType = [u8; 2]; 86 | 87 | #[must_use] 88 | fn to_bytes_le(&self) -> Self::AsBytesType { 89 | (self.0).to_bytes_le() 90 | } 91 | 92 | #[must_use] 93 | fn to_bytes_be(&self) -> Self::AsBytesType { 94 | (self.0).to_bytes_be() 95 | } 96 | 97 | #[must_use] 98 | fn from_bytes_le(bytes: &[u8]) -> Option { 99 | Some(VersionID(u16::from_bytes_le(bytes)?)) 100 | } 101 | 102 | #[must_use] 103 | fn from_bytes_be(bytes: &[u8]) -> Option { 104 | Some(VersionID(u16::from_bytes_be(bytes)?)) 105 | } 106 | } 107 | 108 | /// Minimum number of replay protection list entries 109 | pub struct Crpl(u16); 110 | impl Crpl { 111 | pub const fn byte_len() -> usize { 112 | 2 113 | } 114 | } 115 | pub enum FeatureFlags { 116 | Relay = 0b0001, 117 | Proxy = 0b0010, 118 | Friend = 0b0100, 119 | LowPower = 0b1000, 120 | } 121 | impl From for u16 { 122 | fn from(f: FeatureFlags) -> Self { 123 | f as u16 124 | } 125 | } 126 | impl ToFromBytesEndian for Features { 127 | type AsBytesType = [u8; 2]; 128 | 129 | #[must_use] 130 | fn to_bytes_le(&self) -> Self::AsBytesType { 131 | (self.0).to_bytes_le() 132 | } 133 | 134 | #[must_use] 135 | fn to_bytes_be(&self) -> Self::AsBytesType { 136 | (self.0).to_bytes_be() 137 | } 138 | 139 | #[must_use] 140 | fn from_bytes_le(bytes: &[u8]) -> Option { 141 | Some(Features(u16::from_bytes_le(bytes)?)) 142 | } 143 | 144 | #[must_use] 145 | fn from_bytes_be(bytes: &[u8]) -> Option { 146 | Some(Features(u16::from_bytes_be(bytes)?)) 147 | } 148 | } 149 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 150 | pub struct Features(u16); 151 | impl Features { 152 | pub const fn byte_len() -> usize { 153 | 2 154 | } 155 | pub fn set(&mut self, feature: FeatureFlags) { 156 | self.0 |= u16::from(feature) 157 | } 158 | pub fn clear(&mut self, feature: FeatureFlags) { 159 | self.0 |= !u16::from(feature) 160 | } 161 | #[must_use] 162 | pub fn get(&self, feature: FeatureFlags) -> bool { 163 | self.0 & u16::from(feature) != 0 164 | } 165 | } 166 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 167 | pub struct CRPL(pub u16); 168 | impl CRPL { 169 | pub const fn byte_len() -> usize { 170 | 2 171 | } 172 | } 173 | impl ToFromBytesEndian for CRPL { 174 | type AsBytesType = [u8; 2]; 175 | 176 | #[must_use] 177 | fn to_bytes_le(&self) -> Self::AsBytesType { 178 | (self.0).to_bytes_le() 179 | } 180 | 181 | #[must_use] 182 | fn to_bytes_be(&self) -> Self::AsBytesType { 183 | (self.0).to_bytes_be() 184 | } 185 | 186 | #[must_use] 187 | fn from_bytes_le(bytes: &[u8]) -> Option { 188 | Some(CRPL(u16::from_bytes_le(bytes)?)) 189 | } 190 | 191 | #[must_use] 192 | fn from_bytes_be(bytes: &[u8]) -> Option { 193 | Some(CRPL(u16::from_bytes_be(bytes)?)) 194 | } 195 | } 196 | #[derive(Clone, Ord, PartialOrd, PartialEq, Debug, Hash, Eq)] 197 | pub struct CompositionDataPage0 { 198 | cid: CompanyID, 199 | pid: ProductID, 200 | vid: VersionID, 201 | crpl: CRPL, 202 | features: Features, 203 | elements: ElementsComposition, 204 | } 205 | impl CompositionDataPage0 { 206 | pub fn byte_len(&self) -> usize { 207 | CompanyID::byte_len() 208 | + ProductID::byte_len() 209 | + VersionID::byte_len() 210 | + CRPL::byte_len() 211 | + Features::byte_len() 212 | + self.elements.byte_len() 213 | } 214 | pub const fn min_byte_len() -> usize { 215 | CompanyID::byte_len() 216 | + ProductID::byte_len() 217 | + VersionID::byte_len() 218 | + CRPL::byte_len() 219 | + Features::byte_len() 220 | } 221 | pub fn try_unpack_from(&self, _data: &[u8]) { 222 | unimplemented!() 223 | } 224 | pub fn pack_into(&self, buf: &mut [u8]) { 225 | assert!(buf.len() >= self.byte_len()); 226 | let buf = &mut buf[..self.byte_len()]; 227 | buf[0..2].copy_from_slice(&self.cid.to_bytes_le()); 228 | buf[2..4].copy_from_slice(&self.pid.to_bytes_le()); 229 | buf[4..6].copy_from_slice(&self.vid.to_bytes_le()); 230 | buf[6..8].copy_from_slice(&self.crpl.to_bytes_le()); 231 | buf[8..10].copy_from_slice(&self.features.to_bytes_le()); 232 | self.elements.pack_into(&mut buf[10..]); 233 | } 234 | pub fn as_app_payload(&self) -> AppPayload> { 235 | let mut buf = Vec::with_capacity(self.byte_len()).into_boxed_slice(); 236 | self.pack_into(buf.as_mut()); 237 | AppPayload::new(buf) 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/foundation/model.rs: -------------------------------------------------------------------------------- 1 | use crate::access::ModelIdentifier; 2 | use crate::foundation::publication::ModelPublishInfo; 3 | 4 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] 5 | pub struct ModelComposition { 6 | pub model_identifier: ModelIdentifier, 7 | pub publish_info: ModelPublishInfo, 8 | } 9 | impl ModelComposition { 10 | pub fn is_sig(&self) -> bool { 11 | self.model_identifier.is_sig() 12 | } 13 | pub fn is_vendor(&self) -> bool { 14 | self.model_identifier.is_vendor() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/foundation/publication.rs: -------------------------------------------------------------------------------- 1 | use crate::address::{Address, VirtualAddress}; 2 | use crate::bytes::ToFromBytesEndian; 3 | use crate::mesh::{AppKeyIndex, KeyIndex, TransmitInterval, TTL}; 4 | use crate::uuid::UUID; 5 | use core::convert::TryInto; 6 | use core::time; 7 | 8 | /// 2-bit Step Resoution used for `PublishPeriod`, etc. 9 | #[derive(Copy, Clone, Ord, PartialOrd, Debug, Hash, Eq, PartialEq)] 10 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 11 | pub enum StepResolution { 12 | Milliseconds100 = 0b00, 13 | Second1 = 0b01, 14 | Second10 = 0b10, 15 | Minute10 = 0b11, 16 | } 17 | impl StepResolution { 18 | pub fn to_milliseconds(&self) -> u32 { 19 | match self { 20 | StepResolution::Milliseconds100 => 100, 21 | StepResolution::Second1 => 1000, 22 | StepResolution::Second10 => 10 * 1000, 23 | StepResolution::Minute10 => 10 * 60 * 1000, 24 | } 25 | } 26 | } 27 | impl From for u8 { 28 | fn from(s: StepResolution) -> Self { 29 | s as u8 30 | } 31 | } 32 | const STEPS_MAX: u8 = 0x3F; 33 | /// 6-bit Steps for Periods. 34 | #[derive(Copy, Clone, Ord, PartialOrd, Debug, Hash, Eq, PartialEq)] 35 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 36 | pub struct Steps(u8); 37 | impl Steps { 38 | /// # Panics 39 | /// Panics if `steps == 0` or `steps > STEPS_MAX` 40 | pub fn new(steps: u8) -> Self { 41 | assert!(steps != 0 && steps <= STEPS_MAX); 42 | Self(steps) 43 | } 44 | } 45 | impl From for u8 { 46 | fn from(s: Steps) -> Self { 47 | s.0 48 | } 49 | } 50 | 51 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] 52 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 53 | pub struct PublishPeriod { 54 | pub resolution: StepResolution, 55 | pub steps: Steps, 56 | } 57 | impl PublishPeriod { 58 | pub fn new(resolution: StepResolution, steps: Steps) -> Self { 59 | Self { resolution, steps } 60 | } 61 | pub fn to_milliseconds(&self) -> u32 { 62 | self.resolution.to_milliseconds() * u32::from(self.steps.0) 63 | } 64 | pub fn to_duration(&self) -> time::Duration { 65 | time::Duration::from_millis(self.to_milliseconds().into()) 66 | } 67 | pub fn packed(&self) -> u8 { 68 | u8::from(self.steps) | u8::from(self.resolution) << 6 69 | } 70 | pub fn unpack(b: u8) -> Self { 71 | let steps = Steps::new(b & STEPS_MAX); 72 | let resolution = match b >> 6 { 73 | 0b00 => StepResolution::Milliseconds100, 74 | 0b01 => StepResolution::Second1, 75 | 0b10 => StepResolution::Second10, 76 | 0b11 => StepResolution::Minute10, 77 | _ => unreachable!("step_resolution is only 2-bits"), 78 | }; 79 | Self::new(resolution, steps) 80 | } 81 | } 82 | impl From for u8 { 83 | fn from(p: PublishPeriod) -> Self { 84 | p.packed() 85 | } 86 | } 87 | impl From for time::Duration { 88 | fn from(p: PublishPeriod) -> Self { 89 | p.to_duration() 90 | } 91 | } 92 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 93 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 94 | pub struct PublishRetransmit(pub TransmitInterval); 95 | impl From for PublishRetransmit { 96 | fn from(b: u8) -> Self { 97 | Self(b.into()) 98 | } 99 | } 100 | impl From for u8 { 101 | fn from(retransmit: PublishRetransmit) -> Self { 102 | retransmit.0.into() 103 | } 104 | } 105 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] 106 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 107 | pub struct ModelPublishInfo { 108 | pub address: Address, 109 | pub app_key_index: AppKeyIndex, 110 | pub credential_flag: bool, 111 | pub ttl: Option, // None means default TTL 112 | pub period: PublishPeriod, 113 | pub retransmit: PublishRetransmit, 114 | } 115 | 116 | impl ModelPublishInfo { 117 | pub const NON_VIRTUAL_LEN: usize = 7; 118 | pub const VIRTUAL_LEN: usize = 7 + 14; 119 | pub fn byte_len(&self) -> usize { 120 | if self.address.is_full_virtual() { 121 | Self::VIRTUAL_LEN 122 | } else { 123 | Self::NON_VIRTUAL_LEN 124 | } 125 | } 126 | pub fn unpack(buf: &[u8]) -> Option { 127 | match buf.len() { 128 | Self::NON_VIRTUAL_LEN => { 129 | //Sig NonVirtual 130 | let address = Address::from_bytes_le(&buf[..2])?; 131 | let index = u16::from_bytes_le(&buf[2..4])?; 132 | let credential_flag = index & (1 << 12) != 0; 133 | let publish_ttl = if buf[4] == 0xFF { 134 | None 135 | } else { 136 | if buf[4] & 0x80 != 0 { 137 | return None; 138 | } 139 | Some(TTL::from_masked_u8(buf[4])) 140 | }; 141 | Some(Self { 142 | address, 143 | app_key_index: AppKeyIndex(KeyIndex::new_masked(index)), 144 | credential_flag, 145 | ttl: publish_ttl, 146 | period: PublishPeriod::unpack(buf[5]), 147 | retransmit: PublishRetransmit::from(buf[6]), 148 | }) 149 | } 150 | Self::VIRTUAL_LEN => { 151 | let uuid = UUID((&buf[..16]).try_into().ok()?); 152 | let index = u16::from_bytes_le(&buf[16..18])?; 153 | let credential_flag = index & (1 << 12) != 0; 154 | let publish_ttl = if buf[18] == 0xFF { 155 | None 156 | } else { 157 | if buf[0] & 0x80 != 0 { 158 | return None; 159 | } 160 | Some(TTL::from_masked_u8(buf[18])) 161 | }; 162 | Some(Self { 163 | address: Address::Virtual(VirtualAddress::from(&uuid)), 164 | app_key_index: AppKeyIndex(KeyIndex::new_masked(index)), 165 | credential_flag, 166 | ttl: publish_ttl, 167 | period: PublishPeriod::unpack(buf[19]), 168 | retransmit: PublishRetransmit::from(buf[20]), 169 | }) 170 | } 171 | _ => None, 172 | } 173 | } 174 | pub fn pack_into(&self, buf: &mut [u8]) { 175 | assert!( 176 | buf.len() >= self.byte_len(), 177 | "not enough room for publication" 178 | ); 179 | let address = u16::from(&self.address); 180 | let _pos = match &self.address { 181 | Address::Virtual(va) => { 182 | buf[..16].copy_from_slice(va.uuid().as_ref()); 183 | 16 184 | } 185 | _ => { 186 | buf[..2].copy_from_slice(&address.to_le_bytes()); 187 | 2 188 | } 189 | }; 190 | unimplemented!() 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/foundation/state.rs: -------------------------------------------------------------------------------- 1 | use crate::foundation::FoundationStateError; 2 | use crate::mesh::{TransmitCount, TransmitInterval, TransmitSteps}; 3 | use core::convert::TryFrom; 4 | 5 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 6 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 7 | #[repr(u8)] 8 | pub enum RelayState { 9 | Disabled = 0x00, 10 | Enabled = 0x01, 11 | NotSupported = 0x02, 12 | } 13 | impl From for u8 { 14 | fn from(state: RelayState) -> Self { 15 | state as u8 16 | } 17 | } 18 | impl TryFrom for RelayState { 19 | type Error = FoundationStateError; 20 | 21 | fn try_from(value: u8) -> Result { 22 | match value { 23 | 0x00 => Ok(RelayState::Disabled), 24 | 0x01 => Ok(RelayState::Enabled), 25 | 0x02 => Ok(RelayState::NotSupported), 26 | _ => Err(FoundationStateError(())), 27 | } 28 | } 29 | } 30 | impl Default for RelayState { 31 | fn default() -> Self { 32 | RelayState::Disabled 33 | } 34 | } 35 | impl RelayState { 36 | pub fn is_enabled(self) -> bool { 37 | self == RelayState::Enabled 38 | } 39 | } 40 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 41 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 42 | pub struct RelayRetransmit(pub TransmitInterval); 43 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 44 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 45 | #[repr(u8)] 46 | pub enum SecureNetworkBeaconState { 47 | NotBroadcasting = 0x00, 48 | Broadcasting = 0x01, 49 | } 50 | impl From for u8 { 51 | fn from(state: SecureNetworkBeaconState) -> Self { 52 | state as u8 53 | } 54 | } 55 | impl TryFrom for SecureNetworkBeaconState { 56 | type Error = FoundationStateError; 57 | 58 | fn try_from(value: u8) -> Result { 59 | match value { 60 | 0x00 => Ok(SecureNetworkBeaconState::NotBroadcasting), 61 | 0x01 => Ok(SecureNetworkBeaconState::Broadcasting), 62 | _ => Err(FoundationStateError(())), 63 | } 64 | } 65 | } 66 | impl Default for SecureNetworkBeaconState { 67 | fn default() -> Self { 68 | SecureNetworkBeaconState::NotBroadcasting 69 | } 70 | } 71 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 72 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 73 | #[repr(u8)] 74 | pub enum GATTProxyState { 75 | Disabled = 0x00, 76 | Enabled = 0x01, 77 | NotSupported = 0x02, 78 | } 79 | impl From for u8 { 80 | fn from(state: GATTProxyState) -> Self { 81 | state as u8 82 | } 83 | } 84 | impl TryFrom for GATTProxyState { 85 | type Error = FoundationStateError; 86 | 87 | fn try_from(value: u8) -> Result { 88 | match value { 89 | 0x00 => Ok(GATTProxyState::Disabled), 90 | 0x01 => Ok(GATTProxyState::Enabled), 91 | 0x02 => Ok(GATTProxyState::NotSupported), 92 | _ => Err(FoundationStateError(())), 93 | } 94 | } 95 | } 96 | impl Default for GATTProxyState { 97 | fn default() -> GATTProxyState { 98 | GATTProxyState::Disabled 99 | } 100 | } 101 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 102 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 103 | #[repr(u8)] 104 | pub enum NodeIdentityState { 105 | Stopped = 0x00, 106 | Running = 0x01, 107 | NotSupported = 0x02, 108 | } 109 | 110 | impl From for u8 { 111 | fn from(state: NodeIdentityState) -> Self { 112 | state as u8 113 | } 114 | } 115 | impl TryFrom for NodeIdentityState { 116 | type Error = FoundationStateError; 117 | 118 | fn try_from(value: u8) -> Result { 119 | match value { 120 | 0x00 => Ok(NodeIdentityState::Stopped), 121 | 0x01 => Ok(NodeIdentityState::Running), 122 | 0x02 => Ok(NodeIdentityState::NotSupported), 123 | _ => Err(FoundationStateError(())), 124 | } 125 | } 126 | } 127 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 128 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 129 | #[repr(u8)] 130 | pub enum FriendState { 131 | Disabled = 0x00, 132 | Enabled = 0x01, 133 | NotSupported = 0x02, 134 | } 135 | impl From for u8 { 136 | fn from(state: FriendState) -> Self { 137 | state as u8 138 | } 139 | } 140 | 141 | impl TryFrom for FriendState { 142 | type Error = FoundationStateError; 143 | 144 | fn try_from(value: u8) -> Result { 145 | match value { 146 | 0x00 => Ok(FriendState::Disabled), 147 | 0x01 => Ok(FriendState::Enabled), 148 | 0x02 => Ok(FriendState::NotSupported), 149 | _ => Err(FoundationStateError(())), 150 | } 151 | } 152 | } 153 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 154 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 155 | #[repr(u8)] 156 | pub enum KeyRefreshPhaseState { 157 | Normal = 0x00, 158 | First = 0x01, 159 | Second = 0x02, 160 | } 161 | 162 | impl From for u8 { 163 | fn from(state: KeyRefreshPhaseState) -> Self { 164 | state as u8 165 | } 166 | } 167 | impl TryFrom for KeyRefreshPhaseState { 168 | type Error = FoundationStateError; 169 | 170 | fn try_from(value: u8) -> Result { 171 | match value { 172 | 0x00 => Ok(KeyRefreshPhaseState::Normal), 173 | 0x01 => Ok(KeyRefreshPhaseState::First), 174 | 0x02 => Ok(KeyRefreshPhaseState::Second), 175 | _ => Err(FoundationStateError(())), 176 | } 177 | } 178 | } 179 | /// Used to allow the element to physical get the attention of a person (flashing, beep, etc). 180 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug, Default)] 181 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 182 | pub struct AttentionTimer(pub u8); 183 | impl AttentionTimer { 184 | pub fn new(seconds_remaining: u8) -> Self { 185 | Self(seconds_remaining) 186 | } 187 | pub fn is_off(self) -> bool { 188 | self.0 == 0 189 | } 190 | pub fn is_on(self) -> bool { 191 | !self.is_off() 192 | } 193 | } 194 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 195 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 196 | pub struct DefaultTTLState(u8); 197 | impl DefaultTTLState { 198 | pub fn new(v: u8) -> DefaultTTLState { 199 | match Self::try_new(v) { 200 | None => panic!("bad DefaultTTL given"), 201 | Some(ttl) => ttl, 202 | } 203 | } 204 | /// Tries to create a new `DefaultTTL` from a u8. 205 | /// Will return `None` if `v==0x01 || (0x80 < v <= 0xFF)`. 206 | pub fn try_new(v: u8) -> Option { 207 | match v { 208 | 0x01 => None, 209 | 0x80..=0xFF => None, 210 | _ => Some(DefaultTTLState(v)), 211 | } 212 | } 213 | } 214 | impl From for u8 { 215 | fn from(ttl: DefaultTTLState) -> Self { 216 | ttl.0 217 | } 218 | } 219 | impl Default for DefaultTTLState { 220 | fn default() -> Self { 221 | DefaultTTLState(5) 222 | } 223 | } 224 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 225 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 226 | pub struct DefaultTTLStateError(()); 227 | impl TryFrom for DefaultTTLState { 228 | type Error = DefaultTTLStateError; 229 | 230 | fn try_from(value: u8) -> Result { 231 | Self::try_new(value).ok_or(DefaultTTLStateError(())) 232 | } 233 | } 234 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 235 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 236 | pub struct NetworkTransmit(pub TransmitInterval); 237 | impl NetworkTransmit { 238 | pub fn interval(self) -> core::time::Duration { 239 | core::time::Duration::from_millis(self.0.steps.to_milliseconds(10).into()) 240 | } 241 | } 242 | impl Default for NetworkTransmit { 243 | fn default() -> Self { 244 | NetworkTransmit(TransmitInterval { 245 | count: TransmitCount::new(0x3), 246 | steps: TransmitSteps::new(3), 247 | }) 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/friend.rs: -------------------------------------------------------------------------------- 1 | //! Optional Bluetooth Mesh Friends feature. 2 | use crate::address::UnicastAddress; 3 | use crate::mesh::{IVIndex, IVUpdateFlag, KeyRefreshFlag, U24}; 4 | 5 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 6 | pub struct Flags(u8); 7 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 8 | pub struct FSN(bool); 9 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 10 | pub struct MD(u8); 11 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 12 | pub struct Criteria(u8); 13 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 14 | pub struct ReceiveDelay(u8); 15 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 16 | pub struct PollTimeout(U24); 17 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 18 | pub struct LPNCounter(u16); 19 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 20 | pub enum RSSIFactor { 21 | Factor1 = 0b00, 22 | Factor2 = 0b01, 23 | Factor3 = 0b10, 24 | Factor4 = 0b11, 25 | } 26 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 27 | pub enum ReceiveWindowFactor { 28 | Window1 = 0b00, 29 | Window2 = 0b01, 30 | Window3 = 0b10, 31 | Window4 = 0b11, 32 | } 33 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 34 | pub enum MinQueueSizeLog { 35 | Prohibited = 0b000, 36 | N2 = 0b001, 37 | N4 = 0b010, 38 | N8 = 0b011, 39 | N16 = 0b100, 40 | N32 = 0b101, 41 | N64 = 0b110, 42 | N128 = 0b111, 43 | } 44 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 45 | pub struct FriendPoll { 46 | fsn: FSN, 47 | } 48 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 49 | pub struct FriendUpdate { 50 | key_refresh_flag: KeyRefreshFlag, 51 | iv_update_flag: IVUpdateFlag, 52 | iv_index: IVIndex, 53 | md: MD, 54 | } 55 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 56 | pub struct FriendRequest { 57 | criteria: Criteria, 58 | receive_delay: ReceiveDelay, 59 | poll_timeout: PollTimeout, 60 | previous_address: UnicastAddress, 61 | num_elements: u8, 62 | lpn_counter: LPNCounter, 63 | } 64 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 65 | pub struct FriendClear { 66 | address: UnicastAddress, 67 | counter: LPNCounter, 68 | } 69 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)] 70 | pub struct FriendClearConfirm { 71 | address: UnicastAddress, 72 | counter: LPNCounter, 73 | } 74 | -------------------------------------------------------------------------------- /src/interface.rs: -------------------------------------------------------------------------------- 1 | //! Network Input/Output Interface and Filter. 2 | /* 3 | pub trait InterfaceSink { 4 | fn consume_pdu(&mut self, pdu: &IncomingEncryptedNetworkPDU); 5 | } 6 | pub trait InputInterface { 7 | fn take_sink(&mut self, sink: Sink); 8 | } 9 | 10 | pub struct InputInterfaces { 11 | sink: Sink, 12 | } 13 | impl InputInterfaces { 14 | pub fn new(sink: Sink) -> Self { 15 | Self { sink } 16 | } 17 | pub fn add(&self, interface: &mut dyn InputInterface) { 18 | interface.take_sink(self.sink.clone()) 19 | } 20 | } 21 | pub trait OutputInterface { 22 | fn send_pdu(&mut self, pdu: &OutgoingEncryptedNetworkPDU) -> Result<(), BearerError>; 23 | } 24 | #[derive(Default)] 25 | pub struct OutputInterfaces<'a> { 26 | interfaces: Vec<&'a mut dyn OutputInterface>, 27 | } 28 | impl<'a> OutputInterfaces<'a> { 29 | pub fn new() -> Self { 30 | Self::default() 31 | } 32 | pub fn add_interface<'b: 'a>(&mut self, interface: &'b mut dyn OutputInterface) { 33 | self.interfaces.push(interface) 34 | } 35 | pub fn send_pdu(&mut self, pdu: &OutgoingEncryptedNetworkPDU) -> Result<(), BearerError> { 36 | for interface in self.interfaces.iter_mut() { 37 | (*interface).send_pdu(pdu)? 38 | } 39 | Ok(()) 40 | } 41 | } 42 | */ 43 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Pure Rust Bluetooth Mesh Stack. 2 | //! The layers are designed so they can be put together in different ways to make different stacks 3 | //! (single-threaded, multi-threaded, multi-radio, etc). 4 | //! General stack glue is available in [`stack`] while a prebuilt full-stack requires `std`, 5 | //! `async`, etc. 6 | //! ## How a Bluetooth Mesh Stack works 7 | //! 8 | // No STD disabled until https://github.com/rust-lang/rust/pull/69033 goes stable/nightly. 9 | //#![no_std] 10 | #![deny(rustdoc::broken_intra_doc_links)] 11 | //Might re-enable clippy::restriction later. 12 | #![warn(clippy::all, clippy::pedantic, clippy::nursery, clippy::cargo)] 13 | #![allow( 14 | dead_code, 15 | clippy::cast_possible_truncation, 16 | clippy::use_self, 17 | clippy::doc_markdown, 18 | clippy::module_name_repetitions, 19 | clippy::must_use_candidate, 20 | clippy::missing_errors_doc 21 | )] 22 | 23 | #[cfg(feature = "std")] 24 | #[macro_use] 25 | extern crate std; 26 | 27 | #[cfg(feature = "serde-1")] 28 | extern crate serde; 29 | 30 | extern crate alloc; 31 | extern crate btle; 32 | pub use btle::{bytes, uuid}; 33 | pub use driver_async::asyncs; 34 | pub mod random; 35 | pub mod timestamp; 36 | 37 | pub mod access; 38 | pub mod address; 39 | pub mod beacon; 40 | pub mod control; 41 | pub mod crypto; 42 | pub mod foundation; 43 | pub mod lower; 44 | pub mod mesh; 45 | pub mod net; 46 | pub mod reassembler; 47 | pub mod replay; 48 | pub mod segmenter; 49 | pub mod upper; 50 | 51 | pub mod device_state; 52 | pub mod friend; 53 | pub mod interface; 54 | pub mod relay; 55 | //pub mod mesh_io; 56 | //pub mod advertisement; 57 | pub mod stack; 58 | 59 | pub mod models; 60 | 61 | pub mod provisioning; 62 | 63 | pub mod properties; 64 | 65 | #[cfg(test)] 66 | pub mod samples; 67 | -------------------------------------------------------------------------------- /src/mesh_io.rs: -------------------------------------------------------------------------------- 1 | //! Mesh IO Layer. Generalized IO for PDU and Beacons. 2 | use crate::net::OwnedEncryptedPDU; 3 | use crate::scheduler::TimeQueueSlotKey; 4 | //use crate::timestamp::Timestamp; 5 | use crate::ble::advertisement::{AdStructure, AdStructureDataBuffer, RawAdvertisement}; 6 | use crate::ble::gap::{Advertiser, Scanner}; 7 | use crate::ble::RSSI; 8 | use crate::provisioning::pb_adv::PackedPDU; 9 | use crate::timestamp::TimestampTrait; 10 | use crate::{beacon, net, provisioning}; 11 | use alloc::boxed::Box; 12 | use core::convert::TryFrom; 13 | use core::time::Duration; 14 | 15 | #[derive(Copy, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Debug)] 16 | pub enum PDUType { 17 | Network, 18 | Beacon, 19 | Provision, 20 | URI, 21 | Other(u8), 22 | } 23 | impl PDUType { 24 | #[must_use] 25 | pub fn advertisement_type(self) -> u8 { 26 | match self { 27 | PDUType::Network => 0x2A, 28 | PDUType::Beacon => 0x2B, 29 | PDUType::Provision => 0x29, 30 | PDUType::URI => 0x24, 31 | PDUType::Other(o) => o, 32 | } 33 | } 34 | #[must_use] 35 | pub fn from_advertisement_type(v: u8) -> PDUType { 36 | match v { 37 | 0x2A => PDUType::Network, 38 | 0x2B => PDUType::Beacon, 39 | 0x29 => PDUType::Provision, 40 | 0x24 => PDUType::URI, 41 | _ => PDUType::Other(v), 42 | } 43 | } 44 | #[must_use] 45 | pub fn is_other(self) -> bool { 46 | match self { 47 | Self::Other(_) => true, 48 | _ => false, 49 | } 50 | } 51 | #[must_use] 52 | pub fn is_mesh(self) -> bool { 53 | !self.is_other() 54 | } 55 | } 56 | impl From for u8 { 57 | #[must_use] 58 | fn from(p: PDUType) -> Self { 59 | p.advertisement_type() 60 | } 61 | } 62 | impl From for PDUType { 63 | #[must_use] 64 | fn from(v: u8) -> Self { 65 | Self::from_advertisement_type(v) 66 | } 67 | } 68 | /* 69 | const BLE_ADV_MAX_LEN: usize = 31; 70 | #[derive(Copy, Clone, Hash, Debug, Default)] 71 | pub struct RawAdvertisementPDU { 72 | buffer: [u8; BLE_ADV_MAX_LEN], 73 | length: u8, 74 | } 75 | impl RawAdvertisementPDU { 76 | #[must_use] 77 | pub fn new_with_length(length: usize) -> Self { 78 | assert!( 79 | length <= BLE_ADV_MAX_LEN, 80 | "{} bytes won't fit in one adv packet", 81 | length 82 | ); 83 | Self { 84 | buffer: Default::default(), 85 | length: length as u8, 86 | } 87 | } 88 | #[must_use] 89 | pub fn new(bytes: &[u8]) -> Self { 90 | let mut out = Self::new_with_length(bytes.len()); 91 | out.data_mut().copy_from_slice(bytes); 92 | out 93 | } 94 | #[must_use] 95 | pub fn new_payload(pdu_type: PDUType, payload: &[u8]) -> Self { 96 | let mut out = Self::new_with_length(payload.len() + 1); 97 | out.buffer[0] = pdu_type.into(); 98 | out.data_mut()[1..].copy_from_slice(payload); 99 | out 100 | } 101 | #[must_use] 102 | pub fn pdu_type(&self) -> PDUType { 103 | self.buffer[0].into() 104 | } 105 | 106 | #[must_use] 107 | pub fn len(&self) -> usize { 108 | usize::from(self.length) 109 | } 110 | 111 | #[must_use] 112 | pub const fn is_empty(&self) -> bool { 113 | self.length == 0 114 | } 115 | 116 | #[must_use] 117 | pub fn data(&self) -> &[u8] { 118 | &self.buffer[..self.len()] 119 | } 120 | 121 | #[must_use] 122 | pub fn data_mut(&mut self) -> &mut [u8] { 123 | let l = self.len(); 124 | &mut self.buffer[..l] 125 | } 126 | } 127 | impl AsRef<[u8]> for RawAdvertisementPDU { 128 | #[must_use] 129 | fn as_ref(&self) -> &[u8] { 130 | self.data() 131 | } 132 | } 133 | impl AsMut<[u8]> for RawAdvertisementPDU { 134 | #[must_use] 135 | fn as_mut(&mut self) -> &mut [u8] { 136 | self.data_mut() 137 | } 138 | } 139 | impl TryFrom for EncryptedPDU { 140 | type Error = (); 141 | 142 | fn try_from(value: RawAdvertisementPDU) -> Result { 143 | if value.pdu_type() == PDUType::Network { 144 | Ok(Self::from(value.as_ref())) 145 | } else { 146 | Err(()) 147 | } 148 | } 149 | } 150 | */ 151 | pub enum PDU { 152 | Network(net::OwnedEncryptedPDU), 153 | Beacon(beacon::PackedBeacon), 154 | Provisioning(provisioning::pb_adv::PackedPDU), 155 | } 156 | pub struct TransmitParameters { 157 | interval: Duration, 158 | times: u8, 159 | } 160 | pub struct OutgoingMeshPDU { 161 | transmit_parameters: TransmitParameters, 162 | pdu: PDU, 163 | } 164 | impl AsRef for OutgoingMeshPDU { 165 | fn as_ref(&self) -> &PDU { 166 | &self.pdu 167 | } 168 | } 169 | pub struct IncomingPDU { 170 | pdu: PDU, 171 | rssi: Option, 172 | } 173 | impl AsRef for IncomingPDU { 174 | fn as_ref(&self) -> &PDU { 175 | &self.pdu 176 | } 177 | } 178 | pub struct PDUConversionError(()); 179 | impl TryFrom<&AdStructure> for PDU { 180 | type Error = PDUConversionError; 181 | 182 | fn try_from(value: &AdStructure) -> Result { 183 | match value { 184 | AdStructure::MeshPDU(b) => Ok(PDU::Network( 185 | net::OwnedEncryptedPDU::new(b.as_ref()).ok_or(PDUConversionError(()))?, 186 | )), 187 | AdStructure::MeshBeacon(_b) => unimplemented!(), 188 | AdStructure::MeshProvision(_b) => unimplemented!(), 189 | _ => Err(PDUConversionError(())), 190 | } 191 | } 192 | } 193 | impl From> for AdStructure { 194 | fn from(pdu: net::EncryptedPDU<'_>) -> Self { 195 | AdStructure::MeshPDU(AdStructureDataBuffer::new(pdu.data())) 196 | } 197 | } 198 | impl From<&beacon::PackedBeacon> for AdStructure { 199 | fn from(beacon: &beacon::PackedBeacon) -> Self { 200 | AdStructure::MeshBeacon(AdStructureDataBuffer::new(beacon.as_ref())) 201 | } 202 | } 203 | impl From<&provisioning::pb_adv::PackedPDU> for AdStructure { 204 | fn from(pdu: &PackedPDU) -> Self { 205 | AdStructure::MeshProvision(AdStructureDataBuffer::new(pdu.as_ref())) 206 | } 207 | } 208 | impl From<&PDU> for AdStructure { 209 | fn from(p: &PDU) -> Self { 210 | match p { 211 | PDU::Network(n) => n.into(), 212 | PDU::Beacon(b) => b.into(), 213 | PDU::Provisioning(p) => p.into(), 214 | } 215 | } 216 | } 217 | pub struct MeshPDUQueue { 218 | queue: crate::scheduler::SlottedTimeQueue, 219 | } 220 | pub struct IOError(()); 221 | pub trait IOBearer { 222 | fn on_io_pdu(&mut self, callback: Box); 223 | fn send_io_pdu(&mut self, pdu: &PDU) -> Result<(), IOError>; 224 | } 225 | #[derive(Copy, Clone, Debug, Hash)] 226 | pub struct PDUQueueSlot(TimeQueueSlotKey); 227 | impl MeshPDUQueue { 228 | pub fn add(&mut self, delay: Duration, io_pdu: OutgoingMeshPDU) -> PDUQueueSlot { 229 | PDUQueueSlot(self.queue.push(Timestamp::with_delay(delay), io_pdu)) 230 | } 231 | pub fn cancel(&mut self, slot: PDUQueueSlot) -> Option { 232 | self.queue.remove(slot.0) 233 | } 234 | 235 | pub fn send_ready(&mut self, bearer: &mut impl IOBearer) -> Result<(), IOError> { 236 | while let Some((_, pdu)) = self.queue.pop_ready() { 237 | bearer.send_io_pdu(&pdu.pdu)? 238 | } 239 | Ok(()) 240 | } 241 | } 242 | 243 | pub struct AdvertisementIOBearer { 244 | scanner: S, 245 | advertiser: A, 246 | } 247 | impl AdvertisementIOBearer { 248 | pub fn new(scanner: S, advertiser: A) -> AdvertisementIOBearer { 249 | AdvertisementIOBearer { 250 | scanner, 251 | advertiser, 252 | } 253 | } 254 | } 255 | impl IOBearer for AdvertisementIOBearer { 256 | fn on_io_pdu(&mut self, mut callback: Box) { 257 | self.scanner.on_advertisement(Box::new(move |incoming_adv| { 258 | // Only look at the first AdStructure in the advertisement for now. 259 | let rssi = incoming_adv.rssi(); 260 | if let Some(first_struct) = incoming_adv.adv().iter().next() { 261 | if let Ok(pdu) = PDU::try_from(&first_struct) { 262 | let incoming = IncomingPDU { pdu, rssi }; 263 | callback(&incoming); 264 | } 265 | } 266 | })); 267 | } 268 | 269 | fn send_io_pdu(&mut self, pdu: &PDU) -> Result<(), IOError> { 270 | self.advertiser 271 | .advertise(&RawAdvertisement::from(&pdu.into())) 272 | .map_err(|_| IOError(())) 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/models/generics/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/models/lighting/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/models/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::access::Opcode; 2 | 3 | pub mod config; 4 | pub mod generics; 5 | pub mod lighting; 6 | pub mod sensors; 7 | pub mod state; 8 | pub mod time; 9 | 10 | /// Error when trying to pack a message into a byte buffer. 11 | pub enum MessagePackError { 12 | /// Byte Buffer too small to fit the whole message. 13 | SmallBuffer, 14 | /// Incoming Byte Buffer length doesn't make sense. 15 | BadLength, 16 | /// Incoming Byte Buffer creates an invalid message. 17 | BadBytes, 18 | /// Message can't be packed because the object is in a bad state. 19 | BadState, 20 | } 21 | 22 | /// An Access Message that can be packed into a (little endian) byte buffer. 23 | /// If a message comes in that matches `Opcode`, the stack will try to decode it with 24 | /// `PackableMessage::unpack_from`. 25 | pub trait PackableMessage: Sized { 26 | fn opcode() -> Opcode; 27 | /// Bytes need to fit the entire message in bytes (excluding opcode). 28 | fn message_size(&self) -> usize; 29 | /// Pack the message into the byte buffer (without the opcode). If the length of the buffer is 30 | /// too small or the object is in a bad state, return `MessagePackError`. 31 | fn pack_into(&self, buffer: &mut [u8]) -> Result<(), MessagePackError>; 32 | fn pack_with_opcode(&self, buffer: &mut [u8]) -> Result<(), MessagePackError> { 33 | let opcode = Self::opcode(); 34 | let opcode_len = opcode.byte_len(); 35 | self.pack_into(&mut buffer[opcode_len..opcode_len + self.message_size()])?; 36 | opcode 37 | .pack_into(&mut buffer[..opcode_len]) 38 | .expect("incorrectly formatted opcode"); 39 | Ok(()) 40 | } 41 | /// Unpack the message from the byte buffer (without the opcode). Make sure to check for a valid 42 | /// message or return a `MessagePackError` otherwise. 43 | fn unpack_from(buffer: &[u8]) -> Result; 44 | } 45 | -------------------------------------------------------------------------------- /src/models/model.rs: -------------------------------------------------------------------------------- 1 | pub trait Model {} 2 | pub trait ClientModel: Model {} 3 | pub trait ServerModel: Model {} 4 | pub trait ControllerModel: Model {} 5 | -------------------------------------------------------------------------------- /src/models/sensors/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/models/state.rs: -------------------------------------------------------------------------------- 1 | use crate::access::Opcode; 2 | use crate::models::{MessagePackError, PackableMessage}; 3 | 4 | pub trait State {} 5 | 6 | pub trait StateEndpoint { 7 | type Message: PackableMessage; 8 | fn opcode() -> Opcode; 9 | fn handle_message(&mut self, message: Self::Message); 10 | fn handle_message_bytes(&mut self, bytes: &[u8]) -> Result<(), MessagePackError> { 11 | let msg = Self::Message::unpack_from(bytes)?; 12 | self.handle_message(msg); 13 | Ok(()) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/models/time/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/models/transition.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/src/models/transition.rs -------------------------------------------------------------------------------- /src/properties/characteristics.rs: -------------------------------------------------------------------------------- 1 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 2 | #[repr(u16)] 3 | pub enum Characteristics { 4 | AerobicHeartRateLowerLimit = 0x2A7E, 5 | AerobicHeartRateUpperLimit = 0x2A84, 6 | AerobicThreshold = 0x2A7F, 7 | Age = 0x2A80, 8 | Aggregate = 0x2A5A, 9 | AlertCategoryID = 0x2A43, 10 | AlertCategoryIDBitMask = 0x2A42, 11 | AlertLevel = 0x2A06, 12 | AlertNotificationControlPoint = 0x2A44, 13 | AlertStatus = 0x2A3F, 14 | Altitude = 0x2AB3, 15 | AnaerobicHeartRateLowerLimit = 0x2A81, 16 | AnaerobicHeartRateUpperLimit = 0x2A82, 17 | AnaerobicThreshold = 0x2A83, 18 | Analog = 0x2A58, 19 | AnalogOutput = 0x2A59, 20 | ApparentWindDirection = 0x2A73, 21 | ApparentWindSpeed = 0x2A72, 22 | Appearance = 0x2A01, 23 | BarometricPressureTrend = 0x2AA3, 24 | BatteryLevel = 0x2A19, 25 | BatteryLevelState = 0x2A1B, 26 | BatteryPowerState = 0x2A1A, 27 | BloodPressureFeature = 0x2A49, 28 | BloodPressureMeasurement = 0x2A35, 29 | BodyCompositionFeature = 0x2A9B, 30 | BodyCompositionMeasurement = 0x2A9C, 31 | BodySensorLocation = 0x2A38, 32 | BondManagementControlPoint = 0x2AA4, 33 | BondManagementFeatures = 0x2AA5, 34 | BootKeyboardInputReport = 0x2A22, 35 | BootKeyboardOutputReport = 0x2A32, 36 | BootMouseInput = 0x2B2C, 37 | CGMFeature = 0x2AA8, 38 | CGMMeasurement = 0x2AA7, 39 | CGMSessionRunTime = 0x2AAB, 40 | CGMSessionStartTime = 0x2AAA, 41 | CGMSpecificOpsControlPoint = 0x2AAC, 42 | CGMStatus = 0x2AA9, 43 | CrossTrainerData = 0x2ACE, 44 | CSCFeature = 0x2A5C, 45 | CSCMeasurement = 0x2A5B, 46 | CurrentTime = 0x2A2B, 47 | CyclingPowerControlPoint = 0x2A66, 48 | CyclingPowerFeature = 0x2A65, 49 | CyclingPowerMeasurement = 0x2A63, 50 | CyclingPowerVector = 0x2A64, 51 | DatabaseChangeIncrement = 0x2A99, 52 | DateOfBirth = 0x2A85, 53 | DateOfThresholdAssessment = 0x2A86, 54 | DateTime = 0x2A08, 55 | DateUTC = 0x2AED, 56 | DayDateTime = 0x2A0A, 57 | DayOfWeek = 0x2A09, 58 | DescriptorValueChanged = 0x2A7D, 59 | DewPoint = 0x2A7B, 60 | Digital = 0x2A56, 61 | DigitalOutput = 0x2A57, 62 | DSTOffset = 0x2A0D, 63 | Elevation = 0x2A6C, 64 | Email = 0x2B2E, 65 | ExactTime100 = 0x2A0B, 66 | ExactTime256 = 0x2A0C, 67 | FatBurnHeartRateLowerLimit = 0x2A88, 68 | FatBurnHeartRateUpperLimit = 0x2A89, 69 | FirmwareRevisionString = 0x2A26, 70 | FirstName = 0x2A8A, 71 | FitnessMachineControlPoint = 0x2AD9, 72 | FitnessMachineFeature = 0x2ACC, 73 | FitnessMachineStatus = 0x2ADA, 74 | FiveZoneHeartRateLimits = 0x2A8B, 75 | FloorNumber = 0x2AB2, 76 | CentralAddressResolution = 0x2AA6, 77 | DeviceName = 0x2A00, 78 | PeripheralPreferredConnectionParameters = 0x2A04, 79 | PeripheralPrivacyFlag = 0x2A02, 80 | ReconnectionAddress = 0x2A03, 81 | ServiceChanged = 0x2A05, 82 | Gender = 0x2A8C, 83 | GlucoseFeature = 0x2A51, 84 | GlucoseMeasurement = 0x2A18, 85 | GlucoseMeasurementContext = 0x2A34, 86 | GustFactor = 0x2A74, 87 | HardwareRevisionString = 0x2A27, 88 | HeartRateControlPoint = 0x2A39, 89 | HeartRateMax = 0x2A8D, 90 | HeartRateMeasurement = 0x2A37, 91 | HeatIndex = 0x2A7A, 92 | Height = 0x2A8E, 93 | HIDControlPoint = 0x2A4C, 94 | HIDInformation = 0x2A4A, 95 | HipCircumference = 0x2A8F, 96 | HTTPControlPoint = 0x2ABA, 97 | HTTPEntityBody = 0x2AB9, 98 | HTTPHeaders = 0x2AB7, 99 | HTTPStatusCode = 0x2AB8, 100 | HTTPSSecurity = 0x2ABB, 101 | Humidity = 0x2A6F, 102 | IDDAnnunciationStatus = 0x2B22, 103 | IDDCommandControlPoint = 0x2B25, 104 | IDDCommandData = 0x2B26, 105 | IDDFeatures = 0x2B23, 106 | IDDHistoryData = 0x2B28, 107 | IDDRecordAccessControlPoint = 0x2B27, 108 | IDDStatus = 0x2B21, 109 | IDDStatusChanged = 0x2B20, 110 | IDDStatusReaderControlPoint = 0x2B24, 111 | IEEE11073Part20601RegulatoryCertificationDataList = 0x2A2A, 112 | IndoorBikeData = 0x2AD2, 113 | IndoorPositioningConfiguration = 0x2AAD, 114 | IntermediateCuffPressure = 0x2A36, 115 | IntermediateTemperature = 0x2A1E, 116 | Irradiance = 0x2A77, 117 | Language = 0x2AA2, 118 | LastName = 0x2A90, 119 | Latitude = 0x2AAE, 120 | LNControlPoint = 0x2A6B, 121 | LNFeature = 0x2A6A, 122 | LocalEastCoordinate = 0x2AB1, 123 | LocalNorthCoordinate = 0x2AB0, 124 | LocalTimeInformation = 0x2A0F, 125 | LocationAndSpeedCharacteristic = 0x2A67, 126 | LocationName = 0x2AB5, 127 | Longitude = 0x2AAF, 128 | MagneticDeclination = 0x2A2C, 129 | MagneticFluxDensity2D = 0x2AA0, 130 | MagneticFluxDensity3D = 0x2AA1, 131 | ManufacturerNameString = 0x2A29, 132 | MaximumRecommendedHeartRate = 0x2A91, 133 | MeasurementInterval = 0x2A21, 134 | ModelNumberString = 0x2A24, 135 | Navigation = 0x2A68, 136 | NetworkAvailability = 0x2A3E, 137 | NewAlert = 0x2A46, 138 | ObjectActionControlPoint = 0x2AC5, 139 | ObjectChanged = 0x2AC8, 140 | ObjectFirstCreated = 0x2AC1, 141 | ObjectID = 0x2AC3, 142 | ObjectLastModified = 0x2AC2, 143 | ObjectListControlPoint = 0x2AC6, 144 | ObjectListFilter = 0x2AC7, 145 | ObjectName = 0x2ABE, 146 | ObjectProperties = 0x2AC4, 147 | ObjectSize = 0x2AC0, 148 | ObjectType = 0x2ABF, 149 | OTSFeature = 0x2ABD, 150 | PLXContinuousMeasurementCharacteristic = 0x2A5F, 151 | PLXFeatures = 0x2A60, 152 | PLXSpotCheckMeasurement = 0x2A5E, 153 | PnPID = 0x2A50, 154 | PollenConcentration = 0x2A75, 155 | Position2D = 0x2A2F, 156 | Position3D = 0x2A30, 157 | PositionQuality = 0x2A69, 158 | Pressure = 0x2A6D, 159 | ProtocolMode = 0x2A4E, 160 | PulseOximetryControlPoint = 0x2A62, 161 | Rainfall = 0x2A78, 162 | RCFeature = 0x2B1D, 163 | RCSettings = 0x2B1E, 164 | ReconnectionConfigurationControlPoint = 0x2B1F, 165 | RecordAccessControlPoint = 0x2A52, 166 | ReferenceTimeInformation = 0x2A14, 167 | RegisteredUserCharacteristic = 0x2B37, 168 | Removable = 0x2A3A, 169 | Report = 0x2A4D, 170 | ReportMap = 0x2A4B, 171 | ResolvablePrivateAddressOnly = 0x2AC9, 172 | RestingHeartRate = 0x2A92, 173 | RingerControlPoint = 0x2A40, 174 | RingerSetting = 0x2A41, 175 | RowerData = 0x2AD1, 176 | RSCFeature = 0x2A54, 177 | RSCMeasurement = 0x2A53, 178 | SCControlPoint = 0x2A55, 179 | ScanIntervalWindow = 0x2A4F, 180 | ScanRefresh = 0x2A31, 181 | ScientificTemperatureCelsius = 0x2A3C, 182 | SecondaryTimeZone = 0x2A10, 183 | SensorLocation = 0x2A5D, 184 | SerialNumberString = 0x2A25, 185 | ServiceRequired = 0x2A3B, 186 | SoftwareRevisionString = 0x2A28, 187 | SportTypeForAerobicAndAnaerobicThresholds = 0x2A93, 188 | StairClimberData = 0x2AD0, 189 | StepClimberData = 0x2ACF, 190 | String = 0x2A3D, 191 | SupportedHeartRateRange = 0x2AD7, 192 | SupportedInclinationRange = 0x2AD5, 193 | SupportedNewAlertCategory = 0x2A47, 194 | SupportedPowerRange = 0x2AD8, 195 | SupportedResistanceLevelRange = 0x2AD6, 196 | SupportedSpeedRange = 0x2AD4, 197 | SupportedUnreadAlertCategory = 0x2A48, 198 | SystemID = 0x2A23, 199 | TDSControlPoint = 0x2ABC, 200 | Temperature = 0x2A6E, 201 | TemperatureCelsius = 0x2A1F, 202 | TemperatureFahrenheit = 0x2A20, 203 | TemperatureMeasurement = 0x2A1C, 204 | TemperatureType = 0x2A1D, 205 | ThreeZoneHeartRateLimits = 0x2A94, 206 | TimeAccuracy = 0x2A12, 207 | TimeBroadcast = 0x2A15, 208 | TimeSource = 0x2A13, 209 | TimeUpdateControlPoint = 0x2A16, 210 | TimeUpdateState = 0x2A17, 211 | TimeWithDST = 0x2A11, 212 | TimeZone = 0x2A0E, 213 | TrainingStatus = 0x2AD3, 214 | TreadmillData = 0x2ACD, 215 | TrueWindDirection = 0x2A71, 216 | TrueWindSpeed = 0x2A70, 217 | TwoZoneHeartRateLimit = 0x2A95, 218 | TxPowerLevel = 0x2A07, 219 | Uncertainty = 0x2AB4, 220 | UnreadAlertStatus = 0x2A45, 221 | URI = 0x2AB6, 222 | UserControlPoint = 0x2A9F, 223 | UserIndex = 0x2A9A, 224 | UVIndex = 0x2A76, 225 | VO2Max = 0x2A96, 226 | WaistCircumference = 0x2A97, 227 | Weight = 0x2A98, 228 | WeightMeasurement = 0x2A9D, 229 | WeightScaleFeature = 0x2A9E, 230 | WindChill = 0x2A79, 231 | } 232 | -------------------------------------------------------------------------------- /src/properties/mod.rs: -------------------------------------------------------------------------------- 1 | //! Bluetooth Properties (From GATT). 2 | pub mod characteristics; 3 | pub mod value; 4 | -------------------------------------------------------------------------------- /src/properties/property.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/src/properties/property.rs -------------------------------------------------------------------------------- /src/properties/value.rs: -------------------------------------------------------------------------------- 1 | pub struct Scalar {} 2 | -------------------------------------------------------------------------------- /src/provisioning/beacons.rs: -------------------------------------------------------------------------------- 1 | use crate::beacon; 2 | use crate::uuid::UUID; 3 | use driver_async::time::{Duration, Instant, InstantTrait}; 4 | 5 | #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Ord, PartialOrd)] 6 | pub struct BeaconSource { 7 | pub beacon: beacon::UnprovisionedDeviceBeacon, 8 | pub last_seen: Instant, 9 | } 10 | impl BeaconSource { 11 | pub fn new(beacon: beacon::UnprovisionedDeviceBeacon, last_seen: Instant) -> BeaconSource { 12 | BeaconSource { beacon, last_seen } 13 | } 14 | pub fn new_now(beacon: beacon::UnprovisionedDeviceBeacon) -> BeaconSource { 15 | BeaconSource::new(beacon, Instant::now()) 16 | } 17 | pub fn uuid(&self) -> &UUID { 18 | &self.beacon.uuid 19 | } 20 | pub fn is_expired(&self, timeout: Duration) -> bool { 21 | Instant::now() 22 | .checked_duration_since(self.last_seen) 23 | .map(|d| d > timeout) 24 | .unwrap_or(false) 25 | } 26 | } 27 | #[derive(Clone, PartialOrd, PartialEq, Ord, Eq, Debug, Hash)] 28 | pub struct UnprovisionedBeacons { 29 | pub beacons: Vec>, 30 | pub timeout: Duration, 31 | } 32 | impl UnprovisionedBeacons { 33 | pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30); 34 | pub fn new() -> UnprovisionedBeacons { 35 | Self::with_timeout(Self::DEFAULT_TIMEOUT) 36 | } 37 | pub fn with_timeout(timeout: Duration) -> UnprovisionedBeacons { 38 | UnprovisionedBeacons { 39 | beacons: Vec::new(), 40 | timeout, 41 | } 42 | } 43 | pub fn oldest_instant(&self) -> Instant { 44 | Instant::now() - self.timeout 45 | } 46 | pub fn beacons<'a>(&'a self) -> impl Iterator + 'a { 47 | let oldest = self.oldest_instant(); 48 | self.beacons 49 | .iter() 50 | .filter_map(move |&s| s.filter(move |b| b.last_seen < oldest)) 51 | } 52 | pub fn insert(&mut self, beacon: BeaconSource) -> bool { 53 | for slot in self.beacons.iter_mut() { 54 | if slot.map(|b| b.beacon == beacon.beacon).unwrap_or(true) { 55 | *slot = Some(beacon); 56 | return false; 57 | } 58 | } 59 | let oldest = self.oldest_instant(); 60 | for slot in self.beacons.iter_mut() { 61 | if slot.map(|b| b.last_seen < oldest).unwrap_or(true) { 62 | *slot = Some(beacon); 63 | return true; 64 | } 65 | } 66 | self.beacons.push(Some(beacon)); 67 | return true; 68 | } 69 | pub fn shrink_to_fit(&mut self) { 70 | let oldest = self.oldest_instant(); 71 | let mut furthest_index = 0; 72 | for slot in self.beacons.iter_mut().enumerate() { 73 | if let Some(b) = slot.1 { 74 | if b.last_seen < oldest { 75 | *slot.1 = None 76 | } else { 77 | furthest_index = slot.0; 78 | } 79 | } 80 | } 81 | self.beacons.resize(furthest_index, None); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/provisioning/bearer.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/provisioning/bearer_control.rs: -------------------------------------------------------------------------------- 1 | use crate::provisioning::generic::GPCF; 2 | use crate::uuid::UUID; 3 | use btle::{ConversionError, PackError}; 4 | use core::convert::TryFrom; 5 | use core::fmt::{Display, Error, Formatter}; 6 | use std::convert::TryInto; 7 | 8 | /// Bearer Control Opcodes (8-bits). 9 | /// 0x03-0xFF is RFU 10 | #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Ord, PartialOrd)] 11 | #[repr(u8)] 12 | pub enum Opcode { 13 | LinkOpen = 0x00, 14 | LinkAck = 0x01, 15 | LinkClose = 0x02, 16 | } 17 | impl Opcode { 18 | pub fn with_gpcf(self, gpcf: GPCF) -> u8 { 19 | gpcf.pack_with(self.into()) 20 | } 21 | pub fn from_with_gpcf(value: u8) -> (Option, GPCF) { 22 | ( 23 | match value >> 2 { 24 | 0x00 => Some(Opcode::LinkOpen), 25 | 0x01 => Some(Opcode::LinkAck), 26 | 0x02 => Some(Opcode::LinkClose), 27 | _ => None, 28 | }, 29 | GPCF::from_masked_u2(value), 30 | ) 31 | } 32 | } 33 | impl From for u8 { 34 | fn from(opcode: Opcode) -> Self { 35 | opcode as u8 36 | } 37 | } 38 | impl TryFrom for Opcode { 39 | type Error = ConversionError; 40 | 41 | fn try_from(value: u8) -> Result { 42 | match value { 43 | 0x00 => Ok(Opcode::LinkOpen), 44 | 0x01 => Ok(Opcode::LinkAck), 45 | 0x02 => Ok(Opcode::LinkClose), 46 | _ => Err(ConversionError(())), 47 | } 48 | } 49 | } 50 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 51 | pub struct LinkOpen(pub UUID); 52 | 53 | impl LinkOpen { 54 | pub const BYTE_LEN: usize = 16; 55 | pub fn new(uuid: UUID) -> LinkOpen { 56 | LinkOpen(uuid) 57 | } 58 | pub fn uuid(&self) -> &UUID { 59 | &self.0 60 | } 61 | pub const fn byte_len() -> usize { 62 | 16 63 | } 64 | pub fn pack_into(&self, buf: &mut [u8]) -> Result<(), PackError> { 65 | PackError::expect_length(16, buf)?; 66 | buf.copy_from_slice(self.0.as_ref()); 67 | Ok(()) 68 | } 69 | pub fn unpack_from(buf: &[u8]) -> Result { 70 | PackError::expect_length(Self::BYTE_LEN, buf)?; 71 | Ok(LinkOpen(buf.try_into().expect("length checked above"))) 72 | } 73 | } 74 | impl Display for LinkOpen { 75 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 76 | write!(f, "LinkOpen({})", self.0) 77 | } 78 | } 79 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 80 | pub struct LinkAck(); 81 | impl LinkAck { 82 | pub const BYTE_LEN: usize = 0; 83 | pub const fn byte_len() -> usize { 84 | 0 85 | } 86 | pub fn pack_into(self, buf: &mut [u8]) -> Result<(), PackError> { 87 | PackError::expect_length(Self::BYTE_LEN, buf)?; 88 | Ok(()) 89 | } 90 | pub fn unpack_from(buf: &[u8]) -> Result { 91 | PackError::expect_length(Self::BYTE_LEN, buf)?; 92 | Ok(LinkAck()) 93 | } 94 | } 95 | impl Display for LinkAck { 96 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 97 | f.write_str("LinkAck") 98 | } 99 | } 100 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 101 | pub enum CloseReason { 102 | Success = 0x00, 103 | Timeout = 0x01, 104 | Fail = 0x02, 105 | } 106 | impl From for u8 { 107 | fn from(r: CloseReason) -> Self { 108 | r as u8 109 | } 110 | } 111 | impl TryFrom for CloseReason { 112 | type Error = ConversionError; 113 | 114 | fn try_from(value: u8) -> Result { 115 | match value { 116 | 0x00 => Ok(CloseReason::Success), 117 | 0x01 => Ok(CloseReason::Timeout), 118 | 0x02 => Ok(CloseReason::Fail), 119 | _ => Err(ConversionError(())), 120 | } 121 | } 122 | } 123 | impl Display for CloseReason { 124 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 125 | f.write_str(match self { 126 | CloseReason::Success => "Success", 127 | CloseReason::Timeout => "Timeout", 128 | CloseReason::Fail => "Fail", 129 | }) 130 | } 131 | } 132 | 133 | #[derive(Clone, Copy, Eq, Hash, Ord, PartialOrd, PartialEq, Debug)] 134 | pub struct LinkClose(pub CloseReason); 135 | impl LinkClose { 136 | pub const BYTE_LEN: usize = 1; 137 | pub fn new(reason: CloseReason) -> LinkClose { 138 | Self(reason) 139 | } 140 | pub const fn byte_len() -> usize { 141 | 1 142 | } 143 | pub fn pack_into(self, buf: &mut [u8]) -> Result<(), PackError> { 144 | PackError::expect_length(Self::BYTE_LEN, buf)?; 145 | buf[0] = self.0.into(); 146 | Ok(()) 147 | } 148 | pub fn unpack_from(buf: &[u8]) -> Result { 149 | PackError::expect_length(Self::BYTE_LEN, buf)?; 150 | Ok(LinkClose( 151 | buf[0].try_into().map_err(|_| PackError::bad_index(0))?, 152 | )) 153 | } 154 | } 155 | impl Display for LinkClose { 156 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 157 | write!(f, "LinkClose({})", self.0) 158 | } 159 | } 160 | pub enum BearerControlError {} 161 | #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)] 162 | pub enum PDU { 163 | LinkOpen(LinkOpen), 164 | LinkAck(LinkAck), 165 | LinkClose(LinkClose), 166 | } 167 | impl Display for PDU { 168 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 169 | match self { 170 | PDU::LinkOpen(o) => o.fmt(f), 171 | PDU::LinkAck(a) => a.fmt(f), 172 | PDU::LinkClose(c) => c.fmt(f), 173 | } 174 | } 175 | } 176 | 177 | impl PDU { 178 | pub fn opcode(&self) -> Opcode { 179 | match self { 180 | PDU::LinkOpen(_) => Opcode::LinkOpen, 181 | PDU::LinkAck(_) => Opcode::LinkAck, 182 | PDU::LinkClose(_) => Opcode::LinkClose, 183 | } 184 | } 185 | pub fn byte_len(&self) -> usize { 186 | match self { 187 | PDU::LinkOpen(_) => LinkOpen::BYTE_LEN + 1, 188 | PDU::LinkAck(_) => LinkAck::BYTE_LEN + 1, 189 | PDU::LinkClose(_) => LinkClose::BYTE_LEN + 1, 190 | } 191 | } 192 | pub fn pack_into(&self, buf: &mut [u8]) -> Result<(), PackError> { 193 | let opcode = match self { 194 | PDU::LinkOpen(o) => { 195 | o.pack_into(&mut buf[1..])?; 196 | Opcode::LinkOpen 197 | } 198 | PDU::LinkAck(a) => { 199 | a.pack_into(&mut buf[1..])?; 200 | Opcode::LinkAck 201 | } 202 | PDU::LinkClose(c) => { 203 | c.pack_into(&mut buf[1..])?; 204 | Opcode::LinkClose 205 | } 206 | }; 207 | buf[0] = opcode.into(); 208 | Ok(()) 209 | } 210 | pub fn unpack_from(buf: &[u8]) -> Result { 211 | PackError::atleast_length(1, buf)?; 212 | match Opcode::try_from(buf[0]).map_err(|_| PackError::BadOpcode)? { 213 | Opcode::LinkOpen => Ok(PDU::LinkOpen(LinkOpen::unpack_from(&buf[1..])?)), 214 | Opcode::LinkAck => Ok(PDU::LinkAck(LinkAck::unpack_from(&buf[1..])?)), 215 | Opcode::LinkClose => Ok(PDU::LinkClose(LinkClose::unpack_from(&buf[1..])?)), 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/provisioning/confirmation.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::aes::AESCipher; 2 | use crate::crypto::key::Key; 3 | use crate::crypto::{k1, s1, ECDHSecret, Salt}; 4 | use crate::provisioning::protocol; 5 | use crate::provisioning::protocol::{Confirmation, ProtocolPDU, Random}; 6 | 7 | pub struct Input { 8 | pub invite: protocol::Invite, 9 | pub capabilities: protocol::Capabilities, 10 | pub start: protocol::Start, 11 | pub provisioner_public_key: protocol::PublicKey, 12 | pub device_public_key: protocol::PublicKey, 13 | } 14 | 15 | pub struct InputBuilder { 16 | pub invite: Option, 17 | pub capabilities: Option, 18 | pub start: Option, 19 | pub provisioner_public_key: Option, 20 | pub device_public_key: Option, 21 | } 22 | const CAPABILITIES_POS: usize = protocol::Invite::BYTE_LEN; 23 | const START_POS: usize = CAPABILITIES_POS + protocol::Capabilities::BYTE_LEN; 24 | const PROV_KEY_POS: usize = START_POS + protocol::Start::BYTE_LEN; 25 | const DEVICE_KEY_POS: usize = PROV_KEY_POS + protocol::PublicKey::BYTE_LEN; 26 | 27 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 28 | pub struct ConfirmationSalt(pub Salt); 29 | impl AsRef for ConfirmationSalt { 30 | fn as_ref(&self) -> &Salt { 31 | &self.0 32 | } 33 | } 34 | impl AsRef<[u8]> for ConfirmationSalt { 35 | fn as_ref(&self) -> &[u8] { 36 | self.0.as_ref() 37 | } 38 | } 39 | impl InputBuilder { 40 | pub fn is_ready(&self) -> bool { 41 | self.device_public_key.is_some() 42 | && self.provisioner_public_key.is_some() 43 | && self.start.is_some() 44 | && self.capabilities.is_some() 45 | && self.invite.is_some() 46 | } 47 | pub fn build(&self) -> Option { 48 | Some(Input { 49 | device_public_key: self.device_public_key?, 50 | provisioner_public_key: self.provisioner_public_key?, 51 | start: self.start?, 52 | capabilities: self.capabilities?, 53 | invite: self.invite?, 54 | }) 55 | } 56 | } 57 | impl Input { 58 | pub fn salt(&self) -> ConfirmationSalt { 59 | let mut buf = [0_u8; INPUT_LEN]; 60 | self.invite 61 | .pack(&mut buf[..CAPABILITIES_POS]) 62 | .expect("length hardcoded"); 63 | self.capabilities 64 | .pack(&mut buf[CAPABILITIES_POS..START_POS]) 65 | .expect("length hardcoded"); 66 | self.start 67 | .pack(&mut buf[START_POS..PROV_KEY_POS]) 68 | .expect("length hardcoded"); 69 | self.provisioner_public_key 70 | .pack(&mut buf[PROV_KEY_POS..DEVICE_KEY_POS]) 71 | .expect("length hardcoded"); 72 | self.device_public_key 73 | .pack(&mut buf[DEVICE_KEY_POS..INPUT_LEN]) 74 | .expect("length hardcoded"); 75 | ConfirmationSalt(s1(&buf[..])) 76 | } 77 | } 78 | pub const INPUT_LEN: usize = protocol::Invite::BYTE_LEN 79 | + protocol::Capabilities::BYTE_LEN 80 | + protocol::Start::BYTE_LEN 81 | + protocol::PublicKey::BYTE_LEN * 2; 82 | 83 | pub const AUTH_VALUE_LEN: usize = 16; 84 | 85 | #[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Debug, Default, Hash)] 86 | pub struct AuthValue(pub [u8; AUTH_VALUE_LEN]); 87 | impl AuthValue { 88 | pub const ZEROED: AuthValue = AuthValue([0_u8; AUTH_VALUE_LEN]); 89 | pub const DEFAULT: AuthValue = Self::ZEROED; 90 | } 91 | impl AsRef<[u8]> for AuthValue { 92 | fn as_ref(&self) -> &[u8] { 93 | self.0.as_ref() 94 | } 95 | } 96 | impl AsMut<[u8]> for AuthValue { 97 | fn as_mut(&mut self) -> &mut [u8] { 98 | self.0.as_mut() 99 | } 100 | } 101 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialOrd, PartialEq, Ord)] 102 | pub struct ConfirmationKey(pub Key); 103 | impl ConfirmationKey { 104 | pub fn from_salt_and_secret(salt: &ConfirmationSalt, secret: &ECDHSecret) -> ConfirmationKey { 105 | ConfirmationKey(k1(secret.as_ref(), &salt.0, b"prck")) 106 | } 107 | pub fn confirm_random(&self, random: &Random, auth_value: &AuthValue) -> Confirmation { 108 | Confirmation( 109 | *AESCipher::new(&self.0) 110 | .cmac_slice(&[random.0.as_ref(), auth_value.as_ref()]) 111 | .array_ref(), 112 | ) 113 | } 114 | } 115 | impl AsRef for ConfirmationKey { 116 | fn as_ref(&self) -> &Key { 117 | &self.0 118 | } 119 | } 120 | impl AsRef<[u8]> for ConfirmationKey { 121 | fn as_ref(&self) -> &[u8] { 122 | self.0.as_ref() 123 | } 124 | } 125 | #[cfg(test)] 126 | mod tests { 127 | use super::*; 128 | #[test] 129 | pub fn test_input_len() { 130 | assert_eq!(DEVICE_KEY_POS + protocol::PublicKey::BYTE_LEN, INPUT_LEN) 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/provisioning/data.rs: -------------------------------------------------------------------------------- 1 | use crate::address::{UnicastAddress, ADDRESS_LEN}; 2 | use crate::bytes::ToFromBytesEndian; 3 | use crate::crypto::aes::{AESCipher, MicSize}; 4 | use crate::crypto::key::{NetKey, SessionKey, KEY_LEN}; 5 | use crate::crypto::nonce::SessionNonce; 6 | use crate::crypto::{ECDHSecret, ProvisioningSalt}; 7 | use crate::mesh::{IVIndex, KeyIndex, NetKeyIndex}; 8 | use crate::provisioning::protocol::EncryptedProvisioningData; 9 | use btle::{ConversionError, PackError}; 10 | use core::convert::TryFrom; 11 | pub struct SessionSecurityMaterials { 12 | pub key: SessionKey, 13 | pub nonce: SessionNonce, 14 | } 15 | impl SessionSecurityMaterials { 16 | pub fn new(key: SessionKey, nonce: SessionNonce) -> SessionSecurityMaterials { 17 | SessionSecurityMaterials { key, nonce } 18 | } 19 | pub fn from_secret_salt( 20 | secret: &ECDHSecret, 21 | salt: &ProvisioningSalt, 22 | ) -> SessionSecurityMaterials { 23 | SessionSecurityMaterials { 24 | key: SessionKey::from_secret_salt(secret, salt), 25 | nonce: SessionNonce::from_secret_salt(secret, salt), 26 | } 27 | } 28 | } 29 | #[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Hash, Debug)] 30 | #[repr(u8)] 31 | pub enum Flag { 32 | KeyRefresh = 0, 33 | IVUpdate = 1, 34 | } 35 | pub const FLAGS_MAX: u8 = 0b11; 36 | #[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Default, Debug, Hash)] 37 | pub struct Flags(u8); 38 | impl Flags { 39 | fn flag_bit(flag: Flag) -> u8 { 40 | 1_u8 << (flag as u8) 41 | } 42 | pub fn enable(&mut self, flag: Flag) { 43 | self.0 |= Self::flag_bit(flag) 44 | } 45 | pub fn disable(&mut self, flag: Flag) { 46 | self.0 &= !Self::flag_bit(flag) 47 | } 48 | pub fn get(self, flag: Flag) -> bool { 49 | (self.0 & Self::flag_bit(flag)) != 0 50 | } 51 | } 52 | impl From for u8 { 53 | fn from(f: Flags) -> Self { 54 | f.0 55 | } 56 | } 57 | impl TryFrom for Flags { 58 | type Error = ConversionError; 59 | 60 | fn try_from(value: u8) -> Result { 61 | if value > FLAGS_MAX { 62 | Err(ConversionError(())) 63 | } else { 64 | Ok(Flags(value)) 65 | } 66 | } 67 | } 68 | pub struct ProvisioningData { 69 | pub net_key: NetKey, 70 | pub net_key_index: NetKeyIndex, 71 | pub flags: Flags, 72 | pub iv_index: IVIndex, 73 | pub element_address: UnicastAddress, 74 | } 75 | /// Length of all the fields packed together as bytes (25 bytes). 76 | pub const PACKED_LEN: usize = KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN + ADDRESS_LEN; 77 | impl ProvisioningData { 78 | pub fn packed_unencrypted(&self) -> [u8; PACKED_LEN] { 79 | let mut out = [0_u8; PACKED_LEN]; 80 | out[..KEY_LEN].copy_from_slice(self.net_key.key().as_ref()); 81 | out[KEY_LEN..KEY_LEN + 2].copy_from_slice(&self.net_key_index.0.to_bytes_be()); 82 | out[KEY_LEN + 2] = self.flags.into(); 83 | out[KEY_LEN + 2 + 1..KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN] 84 | .copy_from_slice(&self.iv_index.to_bytes_be()); 85 | out[KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN..] 86 | .copy_from_slice(&self.element_address.to_bytes_be()); 87 | out 88 | } 89 | pub fn unpack_unencrypted(buf: &[u8]) -> Result { 90 | PackError::expect_length(PACKED_LEN, buf)?; 91 | let net_key = NetKey::try_from(&buf[..KEY_LEN]).expect("hard coded length"); 92 | let net_key_index = NetKeyIndex( 93 | KeyIndex::from_bytes_be(&buf[KEY_LEN..KEY_LEN + 2]) 94 | .ok_or(PackError::bad_index(KEY_LEN))?, 95 | ); 96 | let flags = 97 | Flags::try_from(buf[KEY_LEN + 2]).map_err(|_| PackError::bad_index(KEY_LEN + 2))?; 98 | let element_address = 99 | UnicastAddress::from_bytes_be(&buf[KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN..]) 100 | .ok_or(PackError::bad_index(KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN))?; 101 | let iv_index = 102 | IVIndex::from_bytes_be(&buf[KEY_LEN + 2 + 1..KEY_LEN + 2 + 1 + IVIndex::BYTE_LEN]) 103 | .expect("hard coded length"); 104 | Ok(ProvisioningData { 105 | net_key, 106 | net_key_index, 107 | flags, 108 | iv_index, 109 | element_address, 110 | }) 111 | } 112 | pub fn encrypt( 113 | &self, 114 | security_materials: &SessionSecurityMaterials, 115 | ) -> EncryptedProvisioningData { 116 | let mut data = self.packed_unencrypted(); 117 | let mic = AESCipher::new(security_materials.key.as_ref()).ccm_encrypt( 118 | security_materials.nonce.as_ref(), 119 | &[], 120 | data.as_mut(), 121 | MicSize::Big, 122 | ); 123 | EncryptedProvisioningData { data, mic } 124 | } 125 | pub fn decrypt( 126 | security_materials: &SessionSecurityMaterials, 127 | mut encrypted_data: EncryptedProvisioningData, 128 | ) -> Option> { 129 | AESCipher::new(security_materials.key.as_ref()) 130 | .ccm_decrypt( 131 | security_materials.nonce.as_ref(), 132 | &[], 133 | encrypted_data.data.as_mut(), 134 | encrypted_data.mic, 135 | ) 136 | .ok(); 137 | Some(ProvisioningData::unpack_unencrypted( 138 | encrypted_data.data.as_ref(), 139 | )) 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/provisioning/generic_bearer.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/provisioning/generic_link.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/provisioning/mod.rs: -------------------------------------------------------------------------------- 1 | //! Provisioning Layer for Bluetooth Mesh 2 | //! Provisioning is Big Endian. 3 | 4 | pub mod beacons; 5 | pub mod bearer; 6 | pub mod bearer_control; 7 | pub mod confirmation; 8 | pub mod data; 9 | pub mod generic; 10 | pub mod generic_bearer; 11 | pub mod generic_link; 12 | pub mod link; 13 | pub mod pb_adv; 14 | pub mod pb_gatt; 15 | pub mod protocol; 16 | pub mod provisioner; 17 | 18 | pub enum Error { 19 | Closed(bearer_control::CloseReason), 20 | } 21 | -------------------------------------------------------------------------------- /src/provisioning/pb_adv.rs: -------------------------------------------------------------------------------- 1 | //! PB-ADV Provisioning bearer for Bluetooth Mesh 2 | use super::generic; 3 | use crate::provisioning::generic::GENERIC_PDU_MAX_LEN; 4 | use btle::bytes::Storage; 5 | use btle::le::advertisement::AdType; 6 | use btle::{PackError, RSSI}; 7 | use std::convert::TryInto; 8 | 9 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 10 | pub struct LinkID(pub u32); 11 | 12 | impl LinkID { 13 | pub const BYTE_LEN: usize = 4; 14 | pub const fn new(link_id: u32) -> LinkID { 15 | LinkID(link_id) 16 | } 17 | pub fn value(self) -> u32 { 18 | self.0 19 | } 20 | } 21 | impl crate::random::Randomizable for LinkID { 22 | fn random_secure() -> Self { 23 | LinkID(u32::random_secure()) 24 | } 25 | } 26 | const PROVISIONEE_START: u8 = 0x80; 27 | const PROVISIONEE_END: u8 = 0xFF; 28 | 29 | const PROVISIONER_START: u8 = 0; 30 | const PROVISIONER_END: u8 = 0x7F; 31 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 32 | pub struct TransactionNumber(pub u8); 33 | impl TransactionNumber { 34 | pub const BYTE_LEN: usize = 1; 35 | pub const fn new(trans_num: u8) -> TransactionNumber { 36 | TransactionNumber(trans_num) 37 | } 38 | pub const fn value(self) -> u8 { 39 | self.0 40 | } 41 | pub const fn new_provisionee() -> TransactionNumber { 42 | Self::new(PROVISIONEE_START) 43 | } 44 | pub const fn new_provisioner() -> TransactionNumber { 45 | Self::new(PROVISIONER_START) 46 | } 47 | pub fn is_provisionee(self) -> bool { 48 | self.0 >= PROVISIONEE_START && self.0 <= PROVISIONEE_END 49 | } 50 | pub fn is_provisioner(self) -> bool { 51 | self.0 >= PROVISIONER_START && self.0 <= PROVISIONER_END 52 | } 53 | pub fn next(self) -> TransactionNumber { 54 | // Provisionee 0x80-0xFF 55 | // Provisioner 0x00-0x7F 56 | // Check if were at the end of the range and we have to wrap to the start 57 | if self.is_provisionee() { 58 | if self.0 == PROVISIONEE_END { 59 | return Self::new(PROVISIONEE_START); 60 | } 61 | } else if self.0 == PROVISIONER_END { 62 | return Self::new(PROVISIONER_START); 63 | } 64 | Self::new(self.0 + 1) 65 | } 66 | pub fn prev(self) -> TransactionNumber { 67 | // Provisionee 0x80-0xFF 68 | // Provisioner 0x00-0x7F 69 | // Check if were at the end of the range and we have to wrap to the start 70 | if self.is_provisionee() { 71 | if self.0 == PROVISIONEE_START { 72 | return Self::new(PROVISIONEE_END); 73 | } 74 | } else if self.0 == PROVISIONER_START { 75 | return Self::new(PROVISIONER_END); 76 | } 77 | Self::new(self.0 - 1) 78 | } 79 | /// Same as calling `next()` but modifies the TransactionNumber instead of returning a new one 80 | pub fn increment(&mut self) { 81 | let next = self.next(); 82 | *self = next; 83 | } 84 | } 85 | impl From for TransactionNumber { 86 | fn from(b: u8) -> Self { 87 | TransactionNumber(b) 88 | } 89 | } 90 | impl From for u8 { 91 | #[must_use] 92 | fn from(num: TransactionNumber) -> Self { 93 | num.0 94 | } 95 | } 96 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] 97 | pub struct PDU> { 98 | pub link_id: LinkID, 99 | pub transaction_number: TransactionNumber, 100 | pub generic_pdu: generic::PDU, 101 | } 102 | impl> core::fmt::Debug for PDU { 103 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 104 | f.debug_struct("PDU") 105 | .field("link_id", &self.link_id) 106 | .field("transaction_number", &self.transaction_number) 107 | .field("generic_pdu", &self.generic_pdu) 108 | .finish() 109 | } 110 | } 111 | impl> PDU { 112 | pub const HEADER_BYTE_LEN: usize = LinkID::BYTE_LEN + TransactionNumber::BYTE_LEN; 113 | pub const MIN_BYTE_LEN: usize = Self::HEADER_BYTE_LEN + 1; 114 | pub const MAX_BYTE_LEN: usize = Self::HEADER_BYTE_LEN + GENERIC_PDU_MAX_LEN; 115 | pub fn byte_len(&self) -> usize { 116 | LinkID::BYTE_LEN + TransactionNumber::BYTE_LEN + self.generic_pdu.byte_len() 117 | } 118 | pub fn pack_into(&self, buf: &mut [u8]) -> Result<(), PackError> { 119 | PackError::expect_length(self.byte_len(), buf)?; 120 | self.generic_pdu 121 | .pack_into(&mut buf[LinkID::BYTE_LEN + TransactionNumber::BYTE_LEN..])?; 122 | buf[..LinkID::BYTE_LEN].copy_from_slice(self.link_id.0.to_be_bytes().as_ref()); 123 | buf[LinkID::BYTE_LEN] = self.transaction_number.0; 124 | Ok(()) 125 | } 126 | pub fn unpack_from(buf: &[u8]) -> Result 127 | where 128 | B: Storage, 129 | { 130 | PackError::atleast_length(Self::MIN_BYTE_LEN, buf)?; 131 | Ok(PDU { 132 | generic_pdu: generic::PDU::unpack_from(&buf[Self::HEADER_BYTE_LEN..])?, 133 | link_id: LinkID(u32::from_be_bytes( 134 | (&buf[..LinkID::BYTE_LEN]) 135 | .try_into() 136 | .expect("array checked above"), 137 | )), 138 | transaction_number: TransactionNumber(buf[LinkID::BYTE_LEN]), 139 | }) 140 | } 141 | pub fn as_ref(&self) -> PDU<&[u8]> { 142 | PDU { 143 | link_id: self.link_id, 144 | transaction_number: self.transaction_number, 145 | generic_pdu: self.generic_pdu.as_ref(), 146 | } 147 | } 148 | } 149 | impl> btle::le::advertisement::AdStructureType for PDU { 150 | fn ad_type(&self) -> AdType { 151 | AdType::PbAdv 152 | } 153 | 154 | fn byte_len(&self) -> usize { 155 | self.byte_len() 156 | } 157 | 158 | fn pack_into(&self, buf: &mut [u8]) -> Result<(), PackError> { 159 | self.pack_into(buf) 160 | } 161 | } 162 | impl> btle::le::advertisement::UnpackableAdStructType for PDU { 163 | fn unpack_from(ad_type: AdType, buf: &[u8]) -> Result 164 | where 165 | Self: Sized, 166 | { 167 | if ad_type == AdType::PbAdv { 168 | Self::unpack_from(buf) 169 | } else { 170 | Err(PackError::BadOpcode) 171 | } 172 | } 173 | } 174 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] 175 | pub struct IncomingPDU> { 176 | pub pdu: PDU, 177 | pub rssi: Option, 178 | } 179 | impl> core::fmt::Debug for IncomingPDU { 180 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 181 | f.debug_struct("IncomingPDU") 182 | .field("pdu", &self.pdu) 183 | .field("rssi", &self.rssi) 184 | .finish() 185 | } 186 | } 187 | pub struct PackedPDU {} 188 | impl AsRef<[u8]> for PackedPDU { 189 | fn as_ref(&self) -> &[u8] { 190 | unimplemented!() 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/provisioning/pb_gatt.rs: -------------------------------------------------------------------------------- 1 | //! PB-GATT Bearer for Bluetooth Mesh 2 | //! Current not supported in favor of PB-ADV. Will add support later. 3 | -------------------------------------------------------------------------------- /src/random.rs: -------------------------------------------------------------------------------- 1 | //! Random Number generation for the Mesh. 2 | //! Generalized over the rand Library so there's no hard dependencies. 3 | 4 | use rand::distributions::{Distribution, Standard}; 5 | use rand::RngCore; 6 | 7 | pub trait Randomizable: Sized { 8 | /// Generates and returns a random `T`. Currently essentially just an alias for `rand::random` 9 | /// Assume `random` to be not secure! Even though `random` could use a cryptographically secure 10 | /// random number generator behind the scenes, use `random_secure` if you need crypto-random. 11 | fn random() -> Self { 12 | Self::random_secure() 13 | } 14 | /// Generates and returns a cryptographically secure random `T`. 15 | fn random_secure() -> Self; 16 | } 17 | pub fn secure_random_fill_bytes(bytes: &mut [u8]) { 18 | rand::thread_rng().fill_bytes(bytes) 19 | } 20 | impl Randomizable for T 21 | where 22 | Standard: Distribution, 23 | { 24 | fn random_secure() -> Self { 25 | rand::random() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/reassembler.rs: -------------------------------------------------------------------------------- 1 | //! Transport Layer Reassembler. 2 | use crate::crypto::aes::MicSize; 3 | use crate::crypto::{AID, MIC}; 4 | use crate::lower::{BlockAck, SegN, SegO, SegmentedAccessPDU, SegmentedControlPDU}; 5 | 6 | use crate::control::{ControlOpcode, ControlPayload}; 7 | use crate::upper; 8 | use crate::upper::EncryptedAppPayload; 9 | use alloc::vec::Vec; 10 | 11 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 12 | pub enum ReassembleError { 13 | DataTooLong, 14 | SegmentOutOfBounds, 15 | Timeout, 16 | } 17 | 18 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 19 | pub enum LowerHeader { 20 | ControlOpcode(ControlOpcode), 21 | AID(Option), 22 | } 23 | impl LowerHeader { 24 | pub fn is_control(self) -> bool { 25 | match self { 26 | LowerHeader::ControlOpcode(_) => true, 27 | LowerHeader::AID(_) => false, 28 | } 29 | } 30 | pub fn is_access(self) -> bool { 31 | !self.is_control() 32 | } 33 | pub fn opcode(self) -> Option { 34 | match self { 35 | LowerHeader::ControlOpcode(opcode) => Some(opcode), 36 | LowerHeader::AID(_) => None, 37 | } 38 | } 39 | pub fn aid(self) -> Option { 40 | match self { 41 | LowerHeader::ControlOpcode(_) => None, 42 | LowerHeader::AID(aid) => aid, 43 | } 44 | } 45 | } 46 | #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash)] 47 | pub struct ContextHeader { 48 | flag: bool, 49 | seg_o: SegO, 50 | block_ack: BlockAck, 51 | lower_header: LowerHeader, 52 | } 53 | impl ContextHeader { 54 | pub fn new(lower_header: LowerHeader, seg_o: SegO, flag: bool) -> Self { 55 | Self { 56 | lower_header, 57 | seg_o, 58 | flag, 59 | block_ack: BlockAck::ZERO, 60 | } 61 | } 62 | #[must_use] 63 | pub fn all_acked(&self) -> bool { 64 | self.block_ack.all_acked(self.seg_o) 65 | } 66 | #[must_use] 67 | pub fn seg_o(&self) -> SegO { 68 | self.seg_o 69 | } 70 | #[must_use] 71 | pub fn seg_count(&self) -> usize { 72 | usize::from(u8::from(self.seg_o)) + 1_usize 73 | } 74 | #[must_use] 75 | pub const fn block_ack(&self) -> BlockAck { 76 | self.block_ack 77 | } 78 | #[must_use] 79 | pub fn mic_size(&self) -> Option { 80 | if self.lower_header.is_access() { 81 | if self.flag { 82 | Some(MicSize::Big) 83 | } else { 84 | Some(MicSize::Small) 85 | } 86 | } else { 87 | None 88 | } 89 | } 90 | #[must_use] 91 | pub fn lower_header(&self) -> LowerHeader { 92 | self.lower_header 93 | } 94 | #[must_use] 95 | pub fn max_seg_len(&self) -> usize { 96 | if self.lower_header.is_control() { 97 | SegmentedControlPDU::max_seg_len() 98 | } else { 99 | SegmentedAccessPDU::max_seg_len() 100 | } 101 | } 102 | #[must_use] 103 | pub fn seg_pos(&self, seg_n: SegN) -> Option { 104 | let pos = usize::from(u8::from(seg_n)) * self.max_seg_len(); 105 | if pos > self.max_len() { 106 | None 107 | } else { 108 | Some(pos) 109 | } 110 | } 111 | #[must_use] 112 | pub fn max_len(&self) -> usize { 113 | self.max_seg_len() * self.seg_count() 114 | } 115 | #[must_use] 116 | pub fn mic_size_bytes(&self) -> usize { 117 | self.mic_size().map_or(0, MicSize::byte_size) 118 | } 119 | } 120 | #[derive(Clone, Debug)] 121 | pub struct Context { 122 | storage: Vec, 123 | data_len: usize, 124 | header: ContextHeader, 125 | } 126 | impl Context { 127 | pub fn new(header: ContextHeader) -> Self { 128 | let mut storage = Vec::with_capacity(header.max_len()); 129 | storage.resize_with(header.max_len(), u8::default); 130 | Self { 131 | storage, 132 | data_len: 0, 133 | header, 134 | } 135 | } 136 | pub fn data(&self) -> &[u8] { 137 | self.storage.as_ref() 138 | } 139 | pub fn is_ready(&self) -> bool { 140 | self.header.all_acked() 141 | } 142 | pub fn header(&self) -> ContextHeader { 143 | self.header 144 | } 145 | pub fn mic_size(&self) -> Option { 146 | self.header.mic_size() 147 | } 148 | pub fn mic(&self) -> Option { 149 | if !self.is_ready() || self.header.lower_header.is_control() { 150 | None 151 | } else { 152 | Some( 153 | MIC::try_from_bytes_le( 154 | &self.data()[self.data_len..][..self.mic_size()?.byte_size()], 155 | ) 156 | .expect("MIC should be here"), 157 | ) 158 | } 159 | } 160 | pub fn insert_data(&mut self, seg_n: SegN, data: &[u8]) -> Result<(), ReassembleError> { 161 | if data.len() > self.header.max_seg_len() { 162 | Err(ReassembleError::DataTooLong) 163 | } else { 164 | let pos = self 165 | .header 166 | .seg_pos(seg_n) 167 | .ok_or(ReassembleError::SegmentOutOfBounds)?; 168 | self.storage[pos..pos + data.len()].copy_from_slice(data); 169 | self.header.block_ack.set(seg_n.into()); 170 | if u8::from(seg_n) == u8::from(self.header.seg_o) { 171 | // Last Seg 172 | self.data_len = pos + data.len() - self.header.mic_size_bytes(); 173 | } 174 | Ok(()) 175 | } 176 | } 177 | 178 | pub fn finish(mut self) -> Result>, Context> { 179 | if self.is_ready() { 180 | let len = self.data_len; 181 | self.storage.truncate(len); 182 | let mic = self.mic(); 183 | let header = self.header; 184 | let storage = self.storage.into_boxed_slice(); 185 | match header.lower_header { 186 | LowerHeader::ControlOpcode(opcode) => Ok(upper::PDU::Control(ControlPayload { 187 | opcode, 188 | payload: storage, 189 | })), 190 | LowerHeader::AID(aid) => Ok(upper::PDU::Access(EncryptedAppPayload { 191 | data: storage, 192 | mic: mic.expect("mic exists if PDU is ready and access"), 193 | aid, 194 | })), 195 | } 196 | } else { 197 | Err(self) 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/relay.rs: -------------------------------------------------------------------------------- 1 | //! Optional Relay Feature 2 | use crate::mesh::{IVIndex, NetKeyIndex}; 3 | use crate::net; 4 | 5 | pub struct RelayPDU { 6 | pub pdu: net::PDU, 7 | pub iv_index: IVIndex, 8 | pub net_key_index: NetKeyIndex, 9 | } 10 | -------------------------------------------------------------------------------- /src/replay.rs: -------------------------------------------------------------------------------- 1 | //! Replay Cache based on a BTreeMap that keeps track of each ivi and seq per src address. Updating 2 | //! the IVIndex causes a 'Garbage Collection' like effect that will delete any cache entries for 3 | //! any 'too' old IVIndices. 4 | use crate::address::UnicastAddress; 5 | use crate::mesh::{SequenceNumber, IVI}; 6 | 7 | use crate::lower::SeqZero; 8 | use crate::net::PrivateHeader; 9 | use alloc::collections::btree_map::Entry; 10 | use alloc::collections::BTreeMap; 11 | 12 | #[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Hash, Debug)] 13 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 14 | pub struct CacheEntry { 15 | seq: SequenceNumber, 16 | ivi: IVI, 17 | seq_zero: Option, 18 | } 19 | impl CacheEntry { 20 | /// Returns (if seq is old, if seq_zero is old). 21 | pub fn is_old_header( 22 | &self, 23 | ivi: IVI, 24 | seq: SequenceNumber, 25 | seq_zero: Option, 26 | ) -> Option<(bool, bool)> { 27 | if self.ivi == ivi { 28 | let is_old_seq = match (self.seq_zero, seq_zero) { 29 | (Some(old_seq), Some(new_seq)) => old_seq >= new_seq, 30 | _ => false, 31 | }; 32 | Some((self.seq >= seq, is_old_seq)) 33 | } else { 34 | None 35 | } 36 | } 37 | } 38 | impl From> for CacheEntry { 39 | fn from(p: PrivateHeader<'_>) -> Self { 40 | CacheEntry { 41 | seq: p.seq(), 42 | ivi: p.ivi(), 43 | seq_zero: None, 44 | } 45 | } 46 | } 47 | #[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash, Default)] 48 | #[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))] 49 | pub struct Cache { 50 | map: BTreeMap, 51 | } 52 | impl Cache { 53 | pub fn new() -> Cache { 54 | Cache::default() 55 | } 56 | pub fn get_entry(&self, address: UnicastAddress) -> Option<&CacheEntry> { 57 | self.map.get(&address) 58 | } 59 | pub fn is_old_header( 60 | &self, 61 | src: UnicastAddress, 62 | ivi: IVI, 63 | seq: SequenceNumber, 64 | seq_zero: Option, 65 | ) -> Option<(bool, bool)> { 66 | self.get_entry(src)?.is_old_header(ivi, seq, seq_zero) 67 | } 68 | pub fn update_seq_zero(&mut self, src: UnicastAddress, ivi: IVI, seq_zero: SeqZero) { 69 | match self.map.entry(src) { 70 | Entry::Vacant(_) => {} 71 | Entry::Occupied(mut o) => { 72 | if o.get().ivi == ivi { 73 | o.get_mut().seq_zero = Some(seq_zero) 74 | } 75 | } 76 | } 77 | } 78 | /// Returns `true` if the `header` is old or `false` if the `header` is new and valid. 79 | /// If no information about the source of the PDU (Src and Seq), it records the header 80 | /// and returns `false` 81 | pub fn replay_net_check( 82 | &mut self, 83 | src: UnicastAddress, 84 | seq: SequenceNumber, 85 | ivi: IVI, 86 | seq_zero: Option, 87 | ) -> (bool, bool) { 88 | match self.map.entry(src) { 89 | Entry::Vacant(v) => { 90 | v.insert(CacheEntry { 91 | seq, 92 | ivi, 93 | seq_zero: None, 94 | }); 95 | (false, false) 96 | } 97 | Entry::Occupied(mut o) => { 98 | match o.get().is_old_header(ivi, seq, seq_zero) { 99 | None => (false, false), // IVI doesn't match 100 | Some((is_old_seq, is_old_seq_zero)) => { 101 | // If Seq is old, update it 102 | if is_old_seq { 103 | o.insert(CacheEntry { 104 | seq, 105 | ivi, 106 | seq_zero: None, 107 | }); 108 | } 109 | (is_old_seq, is_old_seq_zero) 110 | } 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/samples.rs: -------------------------------------------------------------------------------- 1 | use crate::address::{Address, UnicastAddress, VirtualAddress}; 2 | use crate::crypto::key::{AppKey, DevKey, Key, NetKey}; 3 | use crate::crypto::nonce::AppNonceParts; 4 | use crate::crypto::{aes::MicSize, MIC}; 5 | use crate::mesh::{IVIndex, SequenceNumber, U24}; 6 | use crate::uuid::UUID; 7 | use crate::{mesh, upper}; 8 | use core::str::FromStr; 9 | 10 | fn sample_app_key() -> AppKey { 11 | AppKey::new(Key::from_str("63964771734fbd76e3b40519d1d94a48").expect("from sample data")) 12 | } 13 | fn sample_net_key() -> NetKey { 14 | NetKey::new(Key::from_str("7dd7364cd842ad18c17c2b820c84c3d6").expect("from sample data")) 15 | } 16 | fn sample_dev_key() -> DevKey { 17 | DevKey::new(Key::from_str("9d6dd0e96eb25dc19a40ed9914f8f03f").expect("from sample data")) 18 | } 19 | #[test] 20 | fn message22() { 21 | //let opcode = Opcode::Vendor(VendorOpcode::new(0x15), CompanyID(0x00a)); 22 | let parameters = [0xd5_u8, 0x0a, 0x00, 0x48, 0x65, 0x6c, 0x6c, 0x6f]; 23 | let payload = upper::AppPayload::new(parameters); 24 | let app_key = sample_app_key(); 25 | let dst = VirtualAddress::new(&UUID( 26 | UUID::uuid_bytes_from_str("0073e7e4d8b9440faf8415df4c56c0e1").expect("from sample data"), 27 | )); 28 | assert_eq!( 29 | u16::from(dst.hash()), 30 | 0xb529, 31 | "virtual address hash mismatch" 32 | ); 33 | let parts = AppNonceParts { 34 | aszmic: false, 35 | seq: SequenceNumber(U24::new(0x07080B)), 36 | src: UnicastAddress::new(0x1234), 37 | dst: Address::Virtual(dst), 38 | iv_index: IVIndex(0x12345677), 39 | }; 40 | let expected_nonce: [u8; 13] = 41 | mesh::bytes_str_to_buf("010007080b1234b52912345677").expect("from sample data"); 42 | let nonce = parts.to_nonce(); 43 | assert_eq!( 44 | AsRef::<[u8]>::as_ref(&nonce), 45 | &expected_nonce[..], 46 | "nonce mismatch" 47 | ); 48 | let app_sm = upper::SecurityMaterials::VirtualAddress(nonce, &app_key, app_key.aid(), &dst); 49 | let encrypted = payload.encrypt(&app_sm, MicSize::Small); 50 | let expected_encrypted = [0x38_u8, 0x71, 0xb9, 0x04, 0xd4, 0x31, 0x52, 0x63]; 51 | let expect_mic = MIC::Small(0x16CA48A0); 52 | assert_eq!(encrypted.mic(), expect_mic, "mic mismatch"); 53 | assert_eq!( 54 | encrypted.data(), 55 | &expected_encrypted[..], 56 | "encrypted data mismatch" 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /src/segmenter.rs: -------------------------------------------------------------------------------- 1 | //! Transport Layer Segmenter. 2 | use crate::crypto::MIC; 3 | use crate::lower::{BlockAck, SegN, SegO, SegmentHeader, SegmentedAccessPDU, SeqAuth}; 4 | 5 | use crate::crypto::materials::NetworkKeys; 6 | use crate::device_state::SeqRange; 7 | use crate::mesh::{IVIndex, NetKeyIndex, SequenceNumber, CTL, NID}; 8 | use crate::stack::NetworkHeader; 9 | use crate::{lower, net, upper}; 10 | 11 | use core::cmp::min; 12 | 13 | pub struct UpperSegmenter> { 14 | pub upper_pdu: upper::PDU, 15 | pub seg_o: SegO, 16 | pub seq_auth: SeqAuth, 17 | } 18 | impl> Clone for UpperSegmenter { 19 | fn clone(&self) -> Self { 20 | Self { 21 | upper_pdu: self.upper_pdu.clone(), 22 | seg_o: self.seg_o, 23 | seq_auth: self.seq_auth, 24 | } 25 | } 26 | } 27 | impl> UpperSegmenter { 28 | pub fn new(upper_pdu: upper::PDU, seq_auth: SeqAuth) -> Self { 29 | Self { 30 | seg_o: upper_pdu.seg_o(), 31 | upper_pdu, 32 | seq_auth, 33 | } 34 | } 35 | pub fn iter(&self, block_ack: BlockAck) -> SegmentIterator { 36 | SegmentIterator { 37 | block_ack, 38 | segmenter: self, 39 | seg_n: 0, 40 | } 41 | } 42 | pub fn upper_pdu(&self) -> &upper::PDU { 43 | &self.upper_pdu 44 | } 45 | pub fn seg_o(&self) -> SegO { 46 | self.seg_o 47 | } 48 | pub fn seq_auth(&self) -> SeqAuth { 49 | self.seq_auth 50 | } 51 | pub fn seg_count(&self) -> u8 { 52 | u8::from(self.seg_o) + 1 53 | } 54 | } 55 | 56 | pub struct SegmentIterator<'a, Storage: AsRef<[u8]>> { 57 | block_ack: BlockAck, 58 | segmenter: &'a UpperSegmenter, 59 | seg_n: u8, 60 | } 61 | impl<'a, Storage: AsRef<[u8]>> SegmentIterator<'a, Storage> { 62 | pub fn segment_header(&self) -> SegmentHeader { 63 | let flag = self 64 | .segmenter 65 | .upper_pdu 66 | .mic() 67 | .map_or(false, |mic| mic.is_big()); 68 | SegmentHeader::new( 69 | flag, 70 | self.segmenter.seq_auth.seq_zero(), 71 | self.segmenter.seg_o, 72 | SegN::new(self.seg_n), 73 | ) 74 | } 75 | } 76 | impl<'a, Storage: AsRef<[u8]>> Iterator for SegmentIterator<'a, Storage> { 77 | type Item = lower::SegmentedPDU; 78 | 79 | fn next(&mut self) -> Option { 80 | // Skip acked segments. 81 | while self.block_ack.get(self.seg_n) && self.seg_n < u8::from(self.segmenter.seg_o) { 82 | self.seg_n += 1; 83 | } 84 | if self.seg_n > u8::from(self.segmenter.seg_o) { 85 | None 86 | } else { 87 | let seg_n_out = SegN::new(self.seg_n); 88 | let segment_data = self.segmenter.upper_pdu.seg_n_data(seg_n_out); 89 | let header = self.segment_header(); 90 | match &self.segmenter.upper_pdu { 91 | upper::PDU::Control(control) => { 92 | // ControlPDU 93 | let out = lower::SegmentedControlPDU::new(control.opcode, header, segment_data); 94 | self.seg_n += 1; 95 | Some(lower::SegmentedPDU::Control(out)) 96 | } 97 | upper::PDU::Access(access) => { 98 | if segment_data.len() == SegmentedAccessPDU::max_seg_len() { 99 | let out = lower::SegmentedAccessPDU::new( 100 | access.aid(), 101 | access.mic().is_big().into(), 102 | self.segmenter.seq_auth.seq_zero(), 103 | self.segmenter.seg_o, 104 | seg_n_out, 105 | segment_data, 106 | ); 107 | self.seg_n += 1; 108 | Some(lower::SegmentedPDU::Access(out)) 109 | } else { 110 | let mic = access.mic(); 111 | let seg_len = segment_data.len(); 112 | let mut buf = [0_u8; SegmentedAccessPDU::max_seg_len() + MIC::big_size()]; 113 | buf[..seg_len].copy_from_slice(segment_data); 114 | mic.be_pack_into(&mut buf[seg_len..seg_len + mic.byte_size()]); 115 | let out = lower::SegmentedAccessPDU::new( 116 | access.aid(), 117 | mic.is_big().into(), 118 | self.segmenter.seq_auth.first_seq.into(), 119 | self.segmenter.seg_o, 120 | seg_n_out, 121 | &buf[..min( 122 | seg_len + mic.byte_size(), 123 | SegmentedAccessPDU::max_seg_len(), 124 | )], 125 | ); 126 | self.seg_n += 1; 127 | Some(lower::SegmentedPDU::Access(out)) 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | pub struct NetworkSegments> { 136 | upper_pdu: UpperSegmenter, 137 | seg_o: SegO, 138 | net_key_index: NetKeyIndex, 139 | seq_zero: SequenceNumber, 140 | header: NetworkHeader, 141 | remote_block_ack: BlockAck, 142 | } 143 | impl> NetworkSegments { 144 | pub fn segs_left(&self) -> u32 { 145 | self.remote_block_ack.seg_left(self.seg_o).into() 146 | } 147 | /// Returns an Iterator generating all the Unacked Segmented PDUs. `seq` should have enough 148 | /// `SequenceNumbers` to encrypt all the PDUs. 149 | pub fn network_pdu_iter( 150 | &self, 151 | seq: SeqRange, 152 | nid: NID, 153 | ctl: CTL, 154 | ) -> Option> { 155 | if seq.seqs_lefts() < self.segs_left() { 156 | None 157 | } else { 158 | Some(NetworkPDUIterator { 159 | iter: self.upper_pdu.iter(self.remote_block_ack), 160 | header: self.header, 161 | nid, 162 | ctl, 163 | seq, 164 | }) 165 | } 166 | } 167 | /// Returns an Iterator generating all the Encrypted Unacked Segmented PDUs. `seq` should have enough 168 | /// `SequenceNumbers` to encrypt all the PDUs. 169 | pub fn encrypted_network_pdu_iter<'a>( 170 | &self, 171 | seq: SeqRange, 172 | net_keys: &'a NetworkKeys, 173 | ) -> Option>> { 174 | Some(EncryptedNetworkPDUIterator { 175 | // NID and CTL get updated with the PDUs are encrypted 176 | pdus: self.network_pdu_iter(seq, NID::new(0), CTL(false))?, 177 | iv_index: self.header.iv_index, 178 | net_keys, 179 | }) 180 | } 181 | } 182 | impl> Clone for NetworkSegments { 183 | fn clone(&self) -> Self { 184 | Self { 185 | upper_pdu: self.upper_pdu.clone(), 186 | seg_o: self.seg_o, 187 | net_key_index: self.net_key_index, 188 | seq_zero: self.seq_zero, 189 | header: self.header, 190 | remote_block_ack: self.remote_block_ack, 191 | } 192 | } 193 | } 194 | pub struct NetworkPDUIterator<'a, Storage: AsRef<[u8]>> { 195 | iter: SegmentIterator<'a, Storage>, 196 | header: NetworkHeader, 197 | nid: NID, 198 | ctl: CTL, 199 | seq: SeqRange, 200 | } 201 | impl<'a, Storage: AsRef<[u8]>> Iterator for NetworkPDUIterator<'a, Storage> { 202 | type Item = net::PDU; 203 | 204 | fn next(&mut self) -> Option { 205 | let lower: lower::SegmentedPDU = self.iter.next()?; 206 | 207 | Some(net::PDU { 208 | header: net::Header { 209 | ivi: self.header.iv_index.ivi(), 210 | nid: self.nid, 211 | ctl: self.ctl, 212 | ttl: self.header.ttl, 213 | seq: self 214 | .seq 215 | .next() 216 | .expect("should always have enough seq numbers"), 217 | src: self.header.src, 218 | dst: self.header.dst, 219 | }, 220 | 221 | payload: lower.into(), 222 | }) 223 | } 224 | } 225 | pub struct EncryptedNetworkPDUIterator<'a, PDUIter: Iterator> { 226 | pub pdus: PDUIter, 227 | pub iv_index: IVIndex, 228 | pub net_keys: &'a NetworkKeys, 229 | } 230 | impl<'a, PDUIter: Iterator> EncryptedNetworkPDUIterator<'a, PDUIter> { 231 | pub fn new(pdus: PDUIter, iv_index: IVIndex, net_keys: &'a NetworkKeys) -> Self { 232 | Self { 233 | pdus, 234 | iv_index, 235 | net_keys, 236 | } 237 | } 238 | } 239 | impl<'a, PDUIter: Iterator> Iterator for EncryptedNetworkPDUIterator<'a, PDUIter> { 240 | type Item = net::EncryptedPDU; 241 | 242 | fn next(&mut self) -> Option { 243 | Some( 244 | self.pdus 245 | .next()? 246 | .encrypt(self.net_keys, self.iv_index) 247 | .expect("header wasn't correct"), 248 | ) 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /src/stack/bearer.rs: -------------------------------------------------------------------------------- 1 | //! Bluetooth Mesh Bearers. 2 | use crate::foundation::state::NetworkTransmit; 3 | use crate::mesh::{TransmitCount, TransmitInterval, TransmitSteps}; 4 | use crate::provisioning::{link, pb_adv}; 5 | use crate::{beacon, net}; 6 | use btle::bytes::StaticBuf; 7 | use btle::le::advertisement::{AdType, RawAdvertisement}; 8 | use btle::le::report::{EventType, ReportInfo}; 9 | use btle::{PackError, RSSI}; 10 | 11 | #[derive(Debug)] 12 | pub enum BearerError { 13 | Other(Box), 14 | } 15 | 16 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 17 | pub struct IncomingEncryptedNetworkPDU { 18 | pub encrypted_pdu: net::EncryptedPDU, 19 | pub rssi: Option, 20 | pub dont_relay: bool, 21 | } 22 | impl IncomingEncryptedNetworkPDU { 23 | pub fn from_report_info(report_info: ReportInfo<&[u8]>) -> Option { 24 | if report_info.event_type == EventType::AdvInd { 25 | if let Some(ad_struct) = report_info.data.iter().next() { 26 | if ad_struct.ad_type == AdType::MeshPDU { 27 | return Some(IncomingEncryptedNetworkPDU { 28 | encrypted_pdu: net::EncryptedPDU::new(ad_struct.buf.as_ref())?.to_owned(), 29 | rssi: report_info.rssi, 30 | dont_relay: false, 31 | }); 32 | } 33 | } 34 | } 35 | None 36 | } 37 | } 38 | 39 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 40 | pub struct OutgoingEncryptedNetworkPDU { 41 | pub transmit_parameters: NetworkTransmit, 42 | pub pdu: net::EncryptedPDU, 43 | } 44 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 45 | pub struct IncomingBeacon { 46 | pub beacon: beacon::BeaconPDU, 47 | pub rssi: Option, 48 | } 49 | 50 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 51 | pub struct TransmitInstructions { 52 | /// 0-index times (`0` means 1 time, `1` means 2 times, `2` means 3 times, etc) 53 | pub times: u8, 54 | pub interval: core::time::Duration, 55 | } 56 | impl From for TransmitInstructions { 57 | fn from(t: NetworkTransmit) -> Self { 58 | TransmitInstructions { 59 | times: t.0.count.inner() + 1, 60 | interval: core::time::Duration::from_millis(t.0.steps.to_milliseconds(10).into()), 61 | } 62 | } 63 | } 64 | pub type PBAdvBuf = StaticBuf; 65 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 66 | pub enum OutgoingMessage { 67 | Network(OutgoingEncryptedNetworkPDU), 68 | Beacon(beacon::BeaconPDU), 69 | PBAdv(pb_adv::PDU), 70 | } 71 | impl OutgoingMessage { 72 | pub fn to_raw_advertisement( 73 | &self, 74 | ) -> Result<(RawAdvertisement, TransmitInstructions), PackError> { 75 | let mut out = RawAdvertisement::new(); 76 | Ok(match self { 77 | OutgoingMessage::Network(n) => { 78 | out.insert(&n.pdu)?; 79 | (out, n.transmit_parameters.into()) 80 | } 81 | OutgoingMessage::Beacon(b) => { 82 | //TODO: TransmitInstructions 83 | out.insert(b)?; 84 | ( 85 | out, 86 | NetworkTransmit(TransmitInterval::new( 87 | TransmitCount::new(3), 88 | TransmitSteps::new(2), 89 | )) 90 | .into(), 91 | ) 92 | } 93 | OutgoingMessage::PBAdv(p) => { 94 | //TODO: TransmitInstructions 95 | out.insert(p)?; 96 | ( 97 | out, 98 | NetworkTransmit(TransmitInterval::new( 99 | TransmitCount::new(3), 100 | TransmitSteps::new(1), 101 | )) 102 | .into(), 103 | ) 104 | } 105 | }) 106 | } 107 | } 108 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 109 | pub enum IncomingMessage { 110 | Network(IncomingEncryptedNetworkPDU), 111 | Beacon(IncomingBeacon), 112 | PBAdv(pb_adv::IncomingPDU), 113 | } 114 | impl IncomingMessage { 115 | pub fn from_report_info>(report_info: ReportInfo) -> Option { 116 | if report_info.event_type == EventType::AdvNonconnInd { 117 | if let Some(ad_struct) = report_info.data.iter().next() { 118 | match ad_struct.ad_type { 119 | AdType::MeshPDU => { 120 | Some(IncomingMessage::Network(IncomingEncryptedNetworkPDU { 121 | encrypted_pdu: net::EncryptedPDU::new(ad_struct.buf.as_ref())? 122 | .to_owned(), 123 | rssi: report_info.rssi, 124 | dont_relay: false, 125 | })) 126 | } 127 | AdType::MeshBeacon => Some(IncomingMessage::Beacon(IncomingBeacon { 128 | beacon: beacon::BeaconPDU::unpack_from(ad_struct.buf.as_ref()).ok()?, 129 | rssi: report_info.rssi, 130 | })), 131 | AdType::PbAdv => Some(IncomingMessage::PBAdv(pb_adv::IncomingPDU { 132 | pdu: pb_adv::PDU::unpack_from(ad_struct.buf.as_ref()).ok()?, 133 | rssi: report_info.rssi, 134 | })), 135 | _ => None, 136 | } 137 | } else { 138 | None 139 | } 140 | } else { 141 | None 142 | } 143 | } 144 | pub fn network_pdu(&self) -> Option { 145 | match self { 146 | IncomingMessage::Network(n) => Some(*n), 147 | _ => None, 148 | } 149 | } 150 | pub fn beacon(&self) -> Option { 151 | match self { 152 | IncomingMessage::Beacon(b) => Some(*b), 153 | _ => None, 154 | } 155 | } 156 | pub fn pb_adv( 157 | &self, 158 | ) -> Option>> { 159 | match self { 160 | IncomingMessage::PBAdv(p) => Some(*p), 161 | _ => None, 162 | } 163 | } 164 | } 165 | /// ['IncomingMessage`] or [`OutgoingMessage`] 166 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] 167 | pub enum Message { 168 | Outgoing(OutgoingMessage), 169 | Incoming(IncomingMessage), 170 | } 171 | impl From for Message { 172 | fn from(m: OutgoingMessage) -> Self { 173 | Message::Outgoing(m) 174 | } 175 | } 176 | impl From for Message { 177 | fn from(m: IncomingMessage) -> Self { 178 | Message::Incoming(m) 179 | } 180 | } 181 | /* 182 | pub fn single_shot_advertisement>( 183 | le: &mut btle::hci::adapters::le::LEAdapter, 184 | advertisement: OutgoingAdvertisement, 185 | ) -> Result<(), btle::hci::adapter::Error> { 186 | le.set_advertising_data() 187 | } 188 | */ 189 | #[cfg(test)] 190 | mod tests { 191 | use crate::beacon::BeaconPDU::Unprovisioned; 192 | use crate::beacon::{OOBInformation, URIHash, UnprovisionedDeviceBeacon}; 193 | use crate::stack::bearer::IncomingBeacon; 194 | use crate::stack::bearer::IncomingMessage; 195 | use crate::stack::bearer::IncomingMessage::Beacon; 196 | use crate::uuid::UUID; 197 | use btle::le::advertisement::RawAdvertisement; 198 | use btle::le::report::AddressType::RandomDevice; 199 | use btle::le::report::EventType::AdvNonconnInd; 200 | use btle::le::report::ReportInfo; 201 | use btle::{BTAddress, RSSI}; 202 | 203 | #[test] 204 | pub fn test_beacon() { 205 | assert_eq!( 206 | IncomingMessage::from_report_info(ReportInfo { 207 | event_type: AdvNonconnInd, 208 | address_type: RandomDevice, 209 | address: BTAddress([7, 63, 215, 62, 99, 46,],), 210 | rssi: Some(RSSI::new(-60,),), 211 | data: RawAdvertisement(&[ 212 | 24, 43, 0, 221, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 213 | 0, 214 | ]), 215 | }) 216 | .unwrap(), 217 | Beacon(IncomingBeacon { 218 | beacon: Unprovisioned(UnprovisionedDeviceBeacon { 219 | uuid: UUID([221, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,],), 220 | oob_information: OOBInformation(32,), 221 | uri_hash: Some(URIHash(0,),), 222 | },), 223 | rssi: Some(RSSI::new(-60,),), 224 | },) 225 | ); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/stack/bearers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod advertiser; 2 | -------------------------------------------------------------------------------- /src/stack/element.rs: -------------------------------------------------------------------------------- 1 | //! Element Layer 2 | use crate::address::UnicastAddress; 3 | use crate::mesh::ElementIndex; 4 | use crate::stack::model::Model; 5 | use crate::stack::Stack; 6 | use alloc::boxed::Box; 7 | use alloc::vec::Vec; 8 | use core::borrow::Borrow; 9 | use core::convert::TryInto; 10 | 11 | pub struct Element { 12 | address: UnicastAddress, 13 | models: Vec>, 14 | } 15 | 16 | pub struct ElementRef> { 17 | _marker: core::marker::PhantomData, 18 | stack: Storage, 19 | element_index: ElementIndex, 20 | } 21 | impl> ElementRef { 22 | pub fn new(stack: Storage, element_index: ElementIndex) -> Self { 23 | let count = stack.borrow().element_count(); 24 | assert!( 25 | element_index.0 < count.0, 26 | "out of bounds element_index `{}` >= `{}`", 27 | element_index.0, 28 | count.0 29 | ); 30 | ElementRef { 31 | _marker: core::marker::PhantomData, 32 | stack, 33 | element_index, 34 | } 35 | } 36 | pub fn stack(&self) -> &S { 37 | self.stack.borrow() 38 | } 39 | pub fn element_index(&self) -> ElementIndex { 40 | self.element_index 41 | } 42 | pub fn element_address(&self) -> UnicastAddress { 43 | (u16::from(self.stack().primary_address()) + u16::from(self.element_index.0)) 44 | .try_into() 45 | .expect("invalid stack unicast address range") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/stack/full.rs: -------------------------------------------------------------------------------- 1 | //! Full Bluetooth Mesh Stack. Takes `IncomingEncryptedNetworkPDU`s and `OutgoingMessages` and takes 2 | //! care of all the stack layer between them. 3 | //use crate::interface::{InputInterfaces, InterfaceSink, OutputInterfaces}; 4 | 5 | use crate::replay; 6 | use crate::stack::{incoming, outgoing, RecvError, SendError, StackInternals}; 7 | 8 | use crate::asyncs::sync::{mpsc, Mutex, RwLock}; 9 | use crate::stack::bearer::{IncomingEncryptedNetworkPDU, OutgoingMessage}; 10 | use crate::stack::incoming::Incoming; 11 | use crate::stack::outgoing::Outgoing; 12 | use alloc::sync::Arc; 13 | use core::ops::{Deref, DerefMut}; 14 | pub struct FullStack { 15 | pub replay_cache: Arc>, 16 | pub internals: Arc>, 17 | pub outgoing_bearer: mpsc::Receiver, 18 | pub incoming_bearer: mpsc::Sender, 19 | pub incoming: incoming::Incoming, 20 | pub outgoing: outgoing::Outgoing, 21 | _priv: (), 22 | } 23 | pub enum FullStackError { 24 | SendError(SendError), 25 | RecvError(RecvError), 26 | } 27 | pub const CONTROL_CHANNEL_SIZE: usize = 5; 28 | impl FullStack { 29 | /// Create a new `FullStack` based on `StackInternals` and `replay::Cache`. 30 | /// `StackInternals` holds the `device_state::State` which should be save persistently for the 31 | /// entire time a node is in a Mesh Network. If you lose the `StackInternals`, the node will 32 | /// have to be reprovisioned as a new nodes and the old allocated Unicast Addresses are lost. 33 | pub fn new( 34 | internals: StackInternals, 35 | replay_cache: replay::Cache, 36 | channel_size: usize, 37 | ) -> Self { 38 | let (tx_bearer, rx_bearer) = mpsc::channel(2); 39 | let (tx_incoming_encrypted_net, rx_incoming_encrypted_net) = mpsc::channel(channel_size); 40 | let (tx_outgoing_transport, _rx_outgoing_transport) = mpsc::channel(channel_size); 41 | let (tx_control, _rx_control) = mpsc::channel(CONTROL_CHANNEL_SIZE); 42 | let (tx_access, _rx_access) = mpsc::channel(channel_size); 43 | let (tx_ack, rx_ack) = mpsc::channel(channel_size); 44 | let internals = Arc::new(RwLock::new(internals)); 45 | let replay_cache = Arc::new(Mutex::new(replay_cache)); 46 | 47 | // Encrypted Incoming Network PDU Handler. 48 | 49 | Self { 50 | internals: internals.clone(), 51 | outgoing_bearer: rx_bearer, 52 | incoming_bearer: tx_incoming_encrypted_net, 53 | incoming: Incoming::new( 54 | internals.clone(), 55 | replay_cache.clone(), 56 | rx_incoming_encrypted_net, 57 | tx_outgoing_transport, 58 | tx_ack, 59 | tx_access, 60 | tx_control, 61 | channel_size, 62 | ), 63 | replay_cache, 64 | outgoing: Outgoing::new(internals, rx_ack, tx_bearer), 65 | _priv: (), 66 | } 67 | } 68 | pub async fn feed_network_pdu( 69 | &mut self, 70 | pdu: IncomingEncryptedNetworkPDU, 71 | ) -> Result<(), RecvError> { 72 | self.incoming_bearer 73 | .send(pdu) 74 | .await 75 | .map_err(|_| RecvError::ChannelClosed) 76 | } 77 | pub async fn internals_with(&self, func: impl FnOnce(&StackInternals) -> R) -> R { 78 | func(self.internals.read().await.deref()) 79 | } 80 | pub async fn internals_with_mut(&self, func: impl FnOnce(&mut StackInternals) -> R) -> R { 81 | func(self.internals.write().await.deref_mut()) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/stack/messages.rs: -------------------------------------------------------------------------------- 1 | //! Bluetooth Mesh stack message definitions. Each layer of the stack has different amounts of 2 | //! contexts known to the message. Ex: [`crate::stack::bearer::IncomingEncryptedNetworkPDU`] only knows the `NID` 3 | //! and `IVI` without decrypting while an [`OutgoingLowerTransportMessage`] is one layer away and 4 | //! has the `IVIndex`, `NetKeyIndex`, `dst`, `src`, etc. Instead of passing this extra data as 5 | //! parameters for every function, we just wrap the PDUs. 6 | 7 | use crate::address::{Address, UnicastAddress}; 8 | use crate::crypto::aes::MicSize; 9 | use crate::crypto::nonce::{AppNonce, AppNonceParts, DeviceNonce, DeviceNonceParts}; 10 | use crate::device_state::SeqRange; 11 | use crate::lower::{BlockAck, SegO, SeqAuth}; 12 | use crate::mesh::{AppKeyIndex, ElementIndex, IVIndex, NetKeyIndex, SequenceNumber, NID, TTL}; 13 | use crate::stack::segments; 14 | use crate::upper::{AppPayload, EncryptedAppPayload}; 15 | use crate::{control, lower, net, segmenter, upper}; 16 | use btle::RSSI; 17 | 18 | pub enum MessageKeys { 19 | Device(NetKeyIndex), 20 | App(AppKeyIndex), 21 | } 22 | pub struct OutgoingDestination { 23 | pub dst: Address, 24 | pub ttl: Option, 25 | pub app_key_index: AppKeyIndex, 26 | } 27 | pub struct OutgoingMessage> { 28 | pub app_payload: AppPayload, 29 | pub mic_size: MicSize, 30 | pub force_segment: bool, 31 | pub encryption_key: MessageKeys, 32 | pub iv_index: IVIndex, 33 | pub source_element_index: ElementIndex, 34 | pub dst: Address, 35 | pub ttl: Option, 36 | } 37 | pub struct OutgoingLowerTransportMessage { 38 | pub pdu: lower::PDU, 39 | pub src: UnicastAddress, 40 | pub dst: Address, 41 | pub ttl: Option, 42 | pub seq: Option, 43 | pub iv_index: IVIndex, 44 | pub net_key_index: NetKeyIndex, 45 | } 46 | impl OutgoingLowerTransportMessage { 47 | pub fn net_pdu(&self, nid: NID, seq: SequenceNumber, ttl: TTL) -> net::PDU { 48 | net::PDU { 49 | header: net::Header { 50 | ivi: self.iv_index.ivi(), 51 | nid, 52 | ctl: self.pdu.is_control().into(), 53 | ttl, 54 | seq, 55 | src: self.src, 56 | dst: self.dst, 57 | }, 58 | payload: self.pdu, 59 | } 60 | } 61 | } 62 | impl> OutgoingMessage { 63 | pub fn data_with_mic_len(&self) -> usize { 64 | self.app_payload.0.as_ref().len() + self.mic_size.byte_size() 65 | } 66 | pub fn should_segment(&self) -> bool { 67 | self.force_segment || self.app_payload.should_segment(self.mic_size) 68 | } 69 | pub fn seg_o(&self) -> Option { 70 | if self.should_segment() { 71 | Some(upper::calculate_seg_o( 72 | self.data_with_mic_len(), 73 | lower::SegmentedAccessPDU::max_seg_len(), 74 | )) 75 | } else { 76 | None 77 | } 78 | } 79 | } 80 | pub struct OutgoingUpperTransportMessage> { 81 | pub upper_pdu: upper::PDU, 82 | pub iv_index: IVIndex, 83 | pub seq: SeqRange, 84 | pub seg_count: SegO, 85 | pub net_key_index: NetKeyIndex, 86 | pub src: UnicastAddress, 87 | pub dst: Address, 88 | pub ttl: Option, 89 | } 90 | impl> OutgoingUpperTransportMessage { 91 | pub fn should_segment(&self) -> bool { 92 | self.upper_pdu.should_segment() 93 | } 94 | pub fn into_outgoing_segments(self) -> segments::OutgoingSegments { 95 | debug_assert_eq!( 96 | self.seq.seqs_lefts(), 97 | u32::from(u8::from(self.seg_count)) + 1_u32, 98 | "wrong about of sequence numbers" 99 | ); 100 | segments::OutgoingSegments { 101 | segments: segmenter::UpperSegmenter { 102 | upper_pdu: self.upper_pdu, 103 | seg_o: self.seg_count, 104 | seq_auth: SeqAuth::new(self.seq.start(), self.iv_index), 105 | }, 106 | block_ack: BlockAck::ZERO, 107 | net_key_index: self.net_key_index, 108 | src: self.src, 109 | dst: self.dst, 110 | ttl: self.ttl, 111 | } 112 | } 113 | } 114 | pub struct EncryptedIncomingMessage> { 115 | pub encrypted_app_payload: EncryptedAppPayload, 116 | pub seq: SequenceNumber, 117 | pub seg_count: u8, 118 | pub iv_index: IVIndex, 119 | pub net_key_index: NetKeyIndex, 120 | pub dst: Address, 121 | pub src: UnicastAddress, 122 | pub ttl: Option, 123 | pub rssi: Option, 124 | } 125 | impl> EncryptedIncomingMessage { 126 | pub fn app_nonce_parts(&self) -> AppNonceParts { 127 | AppNonceParts { 128 | aszmic: self.szmic(), 129 | seq: self.seq, 130 | src: self.src, 131 | dst: self.dst, 132 | iv_index: self.iv_index, 133 | } 134 | } 135 | pub fn app_nonce(&self) -> AppNonce { 136 | self.app_nonce_parts().to_nonce() 137 | } 138 | pub fn szmic(&self) -> bool { 139 | self.encrypted_app_payload.mic().is_big() 140 | } 141 | pub fn device_nonce_parts(&self) -> DeviceNonceParts { 142 | DeviceNonceParts { 143 | aszmic: self.szmic(), 144 | seq: self.seq, 145 | src: self.src, 146 | dst: self.dst, 147 | iv_index: self.iv_index, 148 | } 149 | } 150 | pub fn device_nonce(&self) -> DeviceNonce { 151 | self.device_nonce_parts().to_nonce() 152 | } 153 | } 154 | pub struct IncomingControlMessage { 155 | pub control_pdu: control::ControlPDU, 156 | pub src: UnicastAddress, 157 | pub rssi: Option, 158 | pub ttl: Option, 159 | } 160 | pub struct IncomingMessage> { 161 | pub payload: Storage, 162 | pub src: UnicastAddress, 163 | pub dst: Address, 164 | pub seq: SequenceNumber, 165 | pub iv_index: IVIndex, 166 | pub net_key_index: NetKeyIndex, 167 | pub app_key_index: Option, 168 | pub ttl: Option, 169 | pub rssi: Option, 170 | } 171 | #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] 172 | pub struct IncomingNetworkPDU { 173 | pub pdu: net::PDU, 174 | pub net_key_index: NetKeyIndex, 175 | pub iv_index: IVIndex, 176 | pub rssi: Option, 177 | } 178 | pub struct IncomingTransportPDU + AsMut<[u8]>> { 179 | pub upper_pdu: upper::PDU, 180 | pub iv_index: IVIndex, 181 | pub seg_count: u8, 182 | pub seq: SequenceNumber, 183 | pub net_key_index: NetKeyIndex, 184 | pub ttl: Option, 185 | pub rssi: Option, 186 | pub src: UnicastAddress, 187 | pub dst: Address, 188 | } 189 | -------------------------------------------------------------------------------- /src/stack/model.rs: -------------------------------------------------------------------------------- 1 | //! Model layer. 2 | use crate::foundation::publication::ModelPublishInfo; 3 | 4 | pub trait Model {} 5 | 6 | pub struct ModelInfo { 7 | publish: ModelPublishInfo, 8 | } 9 | -------------------------------------------------------------------------------- /src/stack/outgoing.rs: -------------------------------------------------------------------------------- 1 | //! Outgoing PDU handler. 2 | use crate::asyncs::{ 3 | sync::{mpsc, Mutex, RwLock}, 4 | time, 5 | }; 6 | use crate::device_state::SeqRange; 7 | use crate::mesh::{SequenceNumber, CTL}; 8 | use crate::net::Header; 9 | use crate::stack::bearer::{OutgoingEncryptedNetworkPDU, OutgoingMessage}; 10 | use crate::stack::messages::{OutgoingLowerTransportMessage, OutgoingUpperTransportMessage}; 11 | use crate::stack::segments::{IncomingPDU, OutgoingSegments}; 12 | use crate::stack::{segments, SendError, StackInternals}; 13 | use crate::{control, net}; 14 | use alloc::sync::Arc; 15 | use core::time::Duration; 16 | 17 | pub struct Outgoing { 18 | pub outgoing_network: Mutex>, 19 | pub internals: Arc>, 20 | pub ack_rx: Mutex>>, 21 | } 22 | pub const SEND_TIMEOUT_SECS: u64 = 10; 23 | impl Outgoing { 24 | pub fn new( 25 | internals: Arc>, 26 | ack_rx: mpsc::Receiver>, 27 | outgoing: mpsc::Sender, 28 | ) -> Self { 29 | Self { 30 | outgoing_network: Mutex::new(outgoing), 31 | internals, 32 | ack_rx: Mutex::new(ack_rx), 33 | } 34 | } 35 | pub async fn send_upper_transport>( 36 | &self, 37 | _msg: OutgoingUpperTransportMessage, 38 | ) -> Result<(), SendError> { 39 | todo!("implement sending upper transport PDU") 40 | } 41 | pub fn send_timeout(&self) -> Duration { 42 | Duration::from_secs(SEND_TIMEOUT_SECS) 43 | } 44 | pub async fn next_ack>( 45 | segments: &OutgoingSegments, 46 | ack_rx: &mut mpsc::Receiver>, 47 | ) -> Result, SendError> { 48 | loop { 49 | let next_ack = ack_rx.recv().await.ok_or(SendError::ChannelClosed)?; 50 | match segments.is_new_ack(next_ack) { 51 | Ok(is_new) if is_new => return Ok(next_ack), 52 | _ => continue, // Ack doesn't match 53 | }; 54 | } 55 | } 56 | pub async fn send_encrypted_network_pdu( 57 | &self, 58 | outgoing_pdu: OutgoingEncryptedNetworkPDU, 59 | ) -> Result<(), SendError> { 60 | self.outgoing_network 61 | .lock() 62 | .await 63 | .send(OutgoingMessage::Network(outgoing_pdu)) 64 | .await 65 | .ok() 66 | .ok_or(SendError::ChannelClosed) 67 | } 68 | pub async fn send_unsegmented( 69 | &self, 70 | msg: OutgoingLowerTransportMessage, 71 | ) -> Result<(), SendError> { 72 | let internals = self.internals.read().await; 73 | let (pdu, net_sm) = internals.lower_to_net(&msg)?; 74 | let transmit_parameters = internals.device_state.config_states().network_transmit; 75 | // Release the lock on StackInternals. 76 | self.send_encrypted_network_pdu(OutgoingEncryptedNetworkPDU { 77 | transmit_parameters, 78 | pdu: pdu 79 | .encrypt(net_sm.network_keys(), msg.iv_index) 80 | .map_err(|_| SendError::NetEncryptError)?, 81 | }) 82 | .await 83 | } 84 | pub async fn send_segments>( 85 | &self, 86 | msg: segments::OutgoingSegments, 87 | ) -> Result<(), SendError> { 88 | //todo check element_index (src address?) 89 | //todo Lock out SeqCounter 90 | let seq = msg.segments.seq_auth().first_seq; 91 | let internals = self.internals.read().await; 92 | let iv_index = msg.segments.seq_auth().iv_index; 93 | if !internals.is_valid_iv_index(iv_index) { 94 | return Err(SendError::InvalidIVIndex); 95 | } 96 | let ivi = iv_index.ivi(); 97 | let net_sm = internals 98 | .net_keys() 99 | .get_keys(msg.net_key_index) 100 | .ok_or(SendError::InvalidNetKeyIndex)? 101 | .tx_key(); 102 | let nid = net_sm.network_keys().nid(); 103 | let ctl = CTL(msg.segments.upper_pdu.is_control()); 104 | let transmit_parameters = internals.device_state().config_states().network_transmit; 105 | let ttl = msg.ttl.unwrap_or_else(|| internals.default_ttl()); 106 | let mut ack_rx = self.ack_rx.lock().await; 107 | let make_net_header = |seq: SequenceNumber| Header { 108 | ivi, 109 | nid, 110 | ctl, 111 | ttl, 112 | seq, 113 | src: msg.src, 114 | dst: msg.dst, 115 | }; 116 | // Immediately send out the PDUs with the acquired seq range. 117 | for (seg, seq) in msg 118 | .segments 119 | .iter(msg.block_ack) 120 | .zip(SeqRange::new_segs(seq, msg.segments.seg_o())) 121 | { 122 | self.send_encrypted_network_pdu(OutgoingEncryptedNetworkPDU { 123 | transmit_parameters, 124 | pdu: net::PDU { 125 | header: make_net_header(seq), 126 | payload: seg.into(), 127 | } 128 | .encrypt(net_sm.network_keys(), iv_index) 129 | .map_err(|_| SendError::NetEncryptError)?, 130 | }) 131 | .await?; 132 | } 133 | time::timeout(self.send_timeout(), async { 134 | loop { 135 | let _first_ack = Self::next_ack(&msg, &mut ack_rx).await?; 136 | 137 | // Check for a valid ack 138 | todo!() 139 | } 140 | // Allow unreachable_code so we can annotate the async result type. 141 | #[allow(unreachable_code)] 142 | Ok::<(), SendError>(()) 143 | }) 144 | .await 145 | .ok() 146 | .ok_or(SendError::AckTimeout)? 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/stack/transport.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/src/stack/transport.rs -------------------------------------------------------------------------------- /src/stack/watchdog.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndrewGi/BluetoothMeshRust/f2b17cca2bb494ca94af6df78a575376074a23a5/src/stack/watchdog.rs -------------------------------------------------------------------------------- /src/timestamp.rs: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------