pub struct LoraPacketBuilder { /* private fields */ }Expand description
Fluent builder for assembling a LoraPacket field by field.
Pick the message variant first with data,
join_request, join_accept,
or rejoin_request. Then set the per-variant
fields. Finalise with one of:
build_unsigned: emit aLoraPacketwithmic = [0; 4]. Sign later by callingrecalculate_mic_v1_0orrecalculate_mic_v1_1.sign_and_encrypt: encryptFRMPayloadand compute the 1.0 Data MIC.sign_join_request/sign_join_request_v1_1: compute the Join Request MIC.sign_join_accept: compute the Join Accept MIC and encrypt the on-air form.
Every field is optional; required-field validation happens in
build_unsigned based on the selected variant.
Implementations§
Source§impl LoraPacketBuilder
impl LoraPacketBuilder
Sourcepub const fn data(self, direction: Direction, confirmed: bool) -> Self
pub const fn data(self, direction: Direction, confirmed: bool) -> Self
Set message type and direction for a Data message.
Sourcepub const fn join_request(self) -> Self
pub const fn join_request(self) -> Self
Begin a Join Request.
Sourcepub const fn join_accept(self) -> Self
pub const fn join_accept(self) -> Self
Begin a Join Accept.
Sourcepub const fn rejoin_request(self, rejoin_type: u8) -> Self
pub const fn rejoin_request(self, rejoin_type: u8) -> Self
Begin a Rejoin Request with the given type (0, 1, or 2).
Sourcepub const fn join_nonce(self, n: AppNonce) -> Self
pub const fn join_nonce(self, n: AppNonce) -> Self
Set Join Nonce / AppNonce (Join Accept).
Sourcepub const fn dl_settings(self, s: DlSettings) -> Self
pub const fn dl_settings(self, s: DlSettings) -> Self
Set DLSettings (Join Accept).
Sourcepub const fn join_req_type(self, t: u8) -> Self
pub const fn join_req_type(self, t: u8) -> Self
Set JoinReqType (LoRaWAN 1.1 Join Accept MIC context).
Sourcepub const fn rj_count_0(self, count: u16) -> Self
pub const fn rj_count_0(self, count: u16) -> Self
Set RJcount0 (Rejoin Request Type 0 and Type 2).
Stored little-endian on the wire. Defaults to 0 when not set.
Sourcepub const fn rj_count_1(self, count: u16) -> Self
pub const fn rj_count_1(self, count: u16) -> Self
Set RJcount1 (Rejoin Request Type 1).
Stored little-endian on the wire. Defaults to 0 when not set.
Sourcepub fn sign_join_accept(self, app_key: &AppKey) -> Result<(LoraPacket, Vec<u8>)>
pub fn sign_join_accept(self, app_key: &AppKey) -> Result<(LoraPacket, Vec<u8>)>
Build a Join Accept, compute the MIC, and produce the encrypted wire bytes.
Returns (plaintext_packet, encrypted_wire). The plaintext packet has
MIC populated and phy_payload set to the plaintext form. The wire
bytes are what you send over the air; the device will decrypt them back
to the plaintext form.
§Errors
Error::MissingField if required Join Accept fields are missing.
Sourcepub fn sign_join_request(self, app_key: &AppKey) -> Result<LoraPacket>
pub fn sign_join_request(self, app_key: &AppKey) -> Result<LoraPacket>
Build a Join Request and compute its MIC using LoRaWAN 1.0 AppKey.
For LoRaWAN 1.1, use sign_join_request_v1_1
which takes a NwkKey directly.
§Errors
Error::MissingField if required fields are missing.
Sourcepub fn sign_join_request_v1_1(self, nwk_key: &NwkKey) -> Result<LoraPacket>
pub fn sign_join_request_v1_1(self, nwk_key: &NwkKey) -> Result<LoraPacket>
Build a Join Request and compute its MIC using LoRaWAN 1.1 NwkKey.
The CMAC algorithm is identical to 1.0; only the key changes.
§Errors
Error::MissingField if required fields are missing.
Sourcepub fn sign_and_encrypt(
self,
app_s_key: &AppSKey,
nwk_s_key: &NwkSKey,
) -> Result<LoraPacket>
pub fn sign_and_encrypt( self, app_s_key: &AppSKey, nwk_s_key: &NwkSKey, ) -> Result<LoraPacket>
Build a Data packet, encrypt FRMPayload, and compute the
LoRaWAN 1.0 MIC in one shot.
The plaintext payload provided via payload is
encrypted with AppSKey (when FPort > 0) or NwkSKey (when
FPort == 0). The MIC is then calculated under NwkSKey with the
1.0 algorithm.
For 1.1 Data frames, build with build_unsigned,
encrypt manually with Data::encrypt_payload, and sign with
LoraPacket::recalculate_mic_v1_1.
§Errors
crate::Error::MissingFieldif required Data fields are missing.crate::Error::PayloadTooLargeifFRMPayloadexceeds 4080 bytes.- Any error from
build_unsignedorLoraPacket::recalculate_mic_v1_0.
§Examples
use lora_packet::{LoraPacket, Direction, DevAddr, AppSKey, NwkSKey};
let app_s_key = AppSKey::from_slice(&hex::decode("ec925802ae430ca77fd3dd73cb2cc588")?)?;
let nwk_s_key = NwkSKey::from_slice(&hex::decode("44024241ed4ce9a68c6a8bc055233fd3")?)?;
let packet = LoraPacket::builder()
.data(Direction::Uplink, false)
.dev_addr(DevAddr::new([0x49, 0xbe, 0x7d, 0xf1]))
.f_cnt(2)
.f_port(1)
.payload(b"test")
.sign_and_encrypt(&app_s_key, &nwk_s_key)?;
assert_eq!(packet.to_wire(), hex::decode("40f17dbe4900020001954378762b11ff0d")?);Sourcepub fn build_unsigned(self) -> Result<LoraPacket>
pub fn build_unsigned(self) -> Result<LoraPacket>
Finalise the builder into a LoraPacket with MIC set to zero.
Useful when you want to sign and emit in separate steps (e.g. for test
vectors or when the signing keys arrive asynchronously). Call a
sign_* method on the builder, or call
recalculate_mic_v1_0 /
recalculate_mic_v1_1 on the
resulting packet, to fill in the MIC.
§Errors
crate::Error::MissingFieldwhen a required field for the chosenMTypeis missing. The field name is in the error.crate::Error::FOptsTooLongwhen theFOptsvector exceeds the 15-byte wire limit.crate::Error::InvalidRejoinTypewhen the rejoin type is not in {0, 1, 2}.
Trait Implementations§
Source§impl Clone for LoraPacketBuilder
impl Clone for LoraPacketBuilder
Source§fn clone(&self) -> LoraPacketBuilder
fn clone(&self) -> LoraPacketBuilder
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more