pub struct LoraPacket {
pub phy_payload: Vec<u8>,
pub mhdr: Mhdr,
pub mic: [u8; 4],
pub payload: Payload,
}Expand description
A LoRaWAN PHYPayload, parsed into structured fields.
LoraPacket is always exactly one of the five message types described by
Payload. The variant carries every field that is meaningful for that
message type; fields that do not apply are not representable.
Construct from wire bytes with from_wire, or compose
from fields with builder.
§Examples
Parse and dispatch on the message variant:
use lora_packet::{LoraPacket, Payload};
let bytes = hex::decode("40f17dbe4900020001954378762b11ff0d")?;
let packet = LoraPacket::from_wire(&bytes)?;
match &packet.payload {
Payload::Data(d) => {
assert_eq!(d.dev_addr.as_bytes(), &[0x49, 0xbe, 0x7d, 0xf1]);
assert_eq!(d.f_cnt(), 2);
}
Payload::JoinRequest(_) => unreachable!(),
Payload::JoinAccept(_) => unreachable!(),
Payload::RejoinRequest(_) => unreachable!(),
Payload::Proprietary(_) => unreachable!(),
}Fields§
§phy_payload: Vec<u8>Full wire bytes (MHDR + MACPayload + MIC).
Kept around so that MIC re-computation does not need to re-serialise.
to_wire rebuilds these bytes from the typed fields.
mhdr: MhdrMAC header byte.
mic: [u8; 4]4-byte message integrity code (the last 4 bytes of phy_payload).
payload: PayloadType-specific payload fields. See Payload.
Implementations§
Source§impl LoraPacket
impl LoraPacket
Sourcepub const fn is_data(&self) -> bool
pub const fn is_data(&self) -> bool
true for any data frame (confirmed or unconfirmed, uplink or downlink).
Sourcepub fn is_confirmed(&self) -> bool
pub fn is_confirmed(&self) -> bool
true for ConfirmedDataUp or ConfirmedDataDown.
Sourcepub const fn is_join_request(&self) -> bool
pub const fn is_join_request(&self) -> bool
true for Join Request.
Sourcepub const fn is_join_accept(&self) -> bool
pub const fn is_join_accept(&self) -> bool
true for Join Accept.
Sourcepub const fn is_rejoin_request(&self) -> bool
pub const fn is_rejoin_request(&self) -> bool
true for Rejoin Request.
Sourcepub const fn as_data(&self) -> Option<&Data>
pub const fn as_data(&self) -> Option<&Data>
Borrow as Data if this is a data frame, else None.
Sourcepub const fn as_data_mut(&mut self) -> Option<&mut Data>
pub const fn as_data_mut(&mut self) -> Option<&mut Data>
Mutably borrow as Data if this is a data frame.
Sourcepub const fn as_join_request(&self) -> Option<&JoinRequest>
pub const fn as_join_request(&self) -> Option<&JoinRequest>
Borrow as JoinRequest if applicable, else None.
Sourcepub const fn as_join_accept(&self) -> Option<&JoinAccept>
pub const fn as_join_accept(&self) -> Option<&JoinAccept>
Borrow as JoinAccept if applicable, else None.
Sourcepub const fn as_rejoin_request(&self) -> Option<&RejoinRequest>
pub const fn as_rejoin_request(&self) -> Option<&RejoinRequest>
Borrow as RejoinRequest if applicable, else None.
Sourcepub const fn direction(&self) -> Option<Direction>
pub const fn direction(&self) -> Option<Direction>
Direction of this packet on the LoRaWAN network.
JoinRequestis always uplink (device to network server).JoinAcceptis always downlink (network server to device).Datareturns the direction encoded in itsMType.RejoinRequestis always uplink.Payload::Proprietaryhas no protocol-defined direction; this method returnsNonein that case.
§Examples
use lora_packet::{LoraPacket, Direction};
let bytes = hex::decode("40f17dbe4900020001954378762b11ff0d").unwrap();
let packet = LoraPacket::from_wire(&bytes).unwrap();
assert_eq!(packet.direction(), Some(Direction::Uplink));Sourcepub fn verify_mic_v1_0(&self, keys: &V1_0MicKeys<'_>) -> Result<bool>
pub fn verify_mic_v1_0(&self, keys: &V1_0MicKeys<'_>) -> Result<bool>
Verify the MIC using the LoRaWAN 1.0 key set.
Compares against self.mic in constant time
(via subtle::ConstantTimeEq). Returns Ok(true) on match,
Ok(false) on mismatch, and an error only if a required key is
missing from keys.
§Errors
crate::Error::MissingKey if a required key for the message type is
not in keys (e.g. nwk_s_key for a Data frame).
§Examples
use lora_packet::{LoraPacket, NwkSKey, V1_0MicKeys};
let bytes = hex::decode("40f17dbe4900020001954378762b11ff0d")?;
let packet = LoraPacket::from_wire(&bytes)?;
let nwk_s_key = NwkSKey::from_slice(&hex::decode("44024241ed4ce9a68c6a8bc055233fd3")?)?;
let keys = V1_0MicKeys { nwk_s_key: Some(&nwk_s_key), ..Default::default() };
assert!(packet.verify_mic_v1_0(&keys)?);Sourcepub fn verify_mic_v1_1(&self, keys: &V1_1MicKeys<'_>) -> Result<bool>
pub fn verify_mic_v1_1(&self, keys: &V1_1MicKeys<'_>) -> Result<bool>
Verify the MIC using the LoRaWAN 1.1 key set.
1.1 uplinks use a dual-MIC construction with both FNwkSIntKey and
SNwkSIntKey; downlinks use SNwkSIntKey only. Join Accept frames
also need join_eui, dev_nonce, and join_req_type in the keyset.
§Errors
crate::Error::MissingKey if a required key for the message type is
not in keys.
Sourcepub fn calculate_mic_v1_0(&self, keys: &V1_0MicKeys<'_>) -> Result<[u8; 4]>
pub fn calculate_mic_v1_0(&self, keys: &V1_0MicKeys<'_>) -> Result<[u8; 4]>
Calculate the MIC under LoRaWAN 1.0 without overwriting self.mic.
Useful when you want to compare against an expected value without
running Self::verify_mic_v1_0, or when you need to record both the
computed MIC and the received MIC for debugging.
§Errors
crate::Error::MissingKeyif a required key for the message type is not inkeys.crate::Error::UnsupportedForVersionif the payload is a Rejoin Request or Proprietary frame (both 1.1-only).
Sourcepub fn calculate_mic_v1_1(&self, keys: &V1_1MicKeys<'_>) -> Result<[u8; 4]>
pub fn calculate_mic_v1_1(&self, keys: &V1_1MicKeys<'_>) -> Result<[u8; 4]>
Calculate the MIC under LoRaWAN 1.1 without overwriting self.mic.
Dispatches by MType and direction:
- Data uplink: dual-MIC with
FNwkSIntKey+SNwkSIntKey. - Data downlink: single MIC with
SNwkSIntKey. - Join Request:
NwkKey. - Join Accept:
JSIntKey+JoinEUI+DevNonce+JoinReqType. - Rejoin Type 1:
JSIntKey. - Rejoin Type 0/2:
SNwkSIntKey.
§Errors
crate::Error::MissingKey if a required key (or context field) for
the message type is not in keys.
Sourcepub fn recalculate_mic_v1_0(&mut self, keys: &V1_0MicKeys<'_>) -> Result<()>
pub fn recalculate_mic_v1_0(&mut self, keys: &V1_0MicKeys<'_>) -> Result<()>
Recompute and overwrite the MIC under LoRaWAN 1.0.
Also rewrites phy_payload so its trailing 4 bytes match the new MIC.
Use after mutating fields on a parsed packet, or after building an
unsigned packet you want to sign in place.
§Errors
crate::Error::MissingKey if a required key for the message type is
not in keys.
Sourcepub fn recalculate_mic_v1_1(&mut self, keys: &V1_1MicKeys<'_>) -> Result<()>
pub fn recalculate_mic_v1_1(&mut self, keys: &V1_1MicKeys<'_>) -> Result<()>
Recompute and overwrite the MIC under LoRaWAN 1.1.
Also rewrites phy_payload so its trailing 4 bytes match the new MIC.
§Errors
crate::Error::MissingKey if a required key for the message type is
not in keys.
Source§impl LoraPacket
impl LoraPacket
Sourcepub fn from_wire(bytes: &[u8]) -> Result<Self>
pub fn from_wire(bytes: &[u8]) -> Result<Self>
Parse a complete PHYPayload from wire bytes.
This is the primary entry point for inbound traffic. The returned
LoraPacket keeps the full wire bytes in
LoraPacket::phy_payload so that MIC re-computation is cheap.
Join Accept frames are encrypted on the wire and cannot be parsed
directly. Use JoinAccept::decrypt_from_wire first, then either
pass the result to JoinAccept::from_plaintext or work with the
returned plaintext bytes directly.
§Errors
crate::Error::TooShortif the buffer is shorter than the minimum 5 bytes (MHDR + MIC), or shorter than the per-variant minimum.crate::Error::TooLongif the buffer exceeds theLoRaWANPHY maximum of 256 bytes.crate::Error::InvalidMTypeif the MHDR encodes an unknownMType(reserved for forward compatibility).crate::Error::InvalidRejoinTypeif a Rejoin Request type byte is not in {0, 1, 2}.crate::Error::Otherfor a Join Accept input (Join Accept needsdecrypt_from_wire).
§Examples
use lora_packet::{LoraPacket, MType};
let bytes = hex::decode("40f17dbe4900020001954378762b11ff0d")?;
let packet = LoraPacket::from_wire(&bytes)?;
assert!(packet.is_data());
assert_eq!(packet.m_type(), MType::UnconfirmedDataUp);
assert_eq!(packet.mic, [0x2b, 0x11, 0xff, 0x0d]);Sourcepub fn to_wire(&self) -> Vec<u8> ⓘ
pub fn to_wire(&self) -> Vec<u8> ⓘ
Serialise back to wire bytes.
Re-encodes the typed fields into the little-endian wire layout and
appends self.mic unchanged. Call
recalculate_mic_v1_0 /
recalculate_mic_v1_1 first if you have
mutated fields and need a fresh MIC.
For round-trip parsing, the output of from_wire(bytes).to_wire()
equals bytes (subject to MIC bytes being preserved).
Source§impl LoraPacket
impl LoraPacket
Sourcepub fn from_hex(s: &str) -> Result<Self>
pub fn from_hex(s: &str) -> Result<Self>
Parse from a hex-encoded wire frame.
§Errors
crate::Error::Hex for invalid hex; otherwise any error from
LoraPacket::from_wire.
Sourcepub fn from_base64(s: &str) -> Result<Self>
pub fn from_base64(s: &str) -> Result<Self>
Parse from a standard base64-encoded wire frame.
§Errors
crate::Error::Base64 for invalid base64; otherwise any error from
LoraPacket::from_wire.
Source§impl LoraPacket
impl LoraPacket
Sourcepub fn builder() -> LoraPacketBuilder
pub fn builder() -> LoraPacketBuilder
Begin building a packet field by field.
See LoraPacketBuilder for the full surface and finalisation
methods.
§Examples
Build a Data uplink, encrypt, and sign in one expression:
use lora_packet::{LoraPacket, Direction, DevAddr, AppSKey, NwkSKey};
let app_s_key = AppSKey::new([0u8; 16]);
let nwk_s_key = NwkSKey::new([0u8; 16]);
let packet = LoraPacket::builder()
.data(Direction::Uplink, false)
.dev_addr(DevAddr::new([0x49, 0xbe, 0x7d, 0xf1]))
.f_cnt(2)
.f_port(1)
.payload(b"hi")
.sign_and_encrypt(&app_s_key, &nwk_s_key)?;
assert!(packet.is_data());Build and sign a Join Request:
use lora_packet::{LoraPacket, AppKey, AppEui, DevEui, DevNonce};
let app_key = AppKey::new([0u8; 16]);
let packet = LoraPacket::builder()
.join_request()
.join_eui(AppEui::new([0u8; 8]))
.dev_eui(DevEui::new([0u8; 8]))
.dev_nonce(DevNonce::new([0u8; 2]))
.sign_join_request(&app_key)?;
assert!(packet.is_join_request());Trait Implementations§
Source§impl Clone for LoraPacket
impl Clone for LoraPacket
Source§fn clone(&self) -> LoraPacket
fn clone(&self) -> LoraPacket
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for LoraPacket
impl Debug for LoraPacket
Source§impl<'de> Deserialize<'de> for LoraPacket
impl<'de> Deserialize<'de> for LoraPacket
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl PartialEq for LoraPacket
impl PartialEq for LoraPacket
Source§fn eq(&self, other: &LoraPacket) -> bool
fn eq(&self, other: &LoraPacket) -> bool
self and other values to be equal, and is used by ==.