Expand description
§lora-packet
LoRaWAN 1.0 and 1.1 packet codec for Rust. Parses and builds PHYPayload
frames, performs AES-ECB FRMPayload and FOpts crypt, computes AES-CMAC
MICs, and derives OTAA, Join Server, and WOR (relay) keys.
- Works on
stdand onno_std + alloctargets (one feature flag away). - No
unsafe; constant-time MIC compares; keys auto-zeroize on drop. - Strong newtypes for every key and identifier so a
NwkSKeycannot be confused with anAppSKeyor with raw[u8; 16]bytes at compile time.
See the README for a wider
introduction and the per-module pages for full API references.
§Quick start: parse, verify, decrypt
use lora_packet::{LoraPacket, AppSKey, 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 app_s_key = AppSKey::from_slice(&hex::decode("ec925802ae430ca77fd3dd73cb2cc588")?)?;
let keys = V1_0MicKeys { nwk_s_key: Some(&nwk_s_key), ..Default::default() };
assert!(packet.verify_mic_v1_0(&keys)?);
let data = packet.as_data().expect("data frame");
let plaintext = data.decrypt_payload(&app_s_key, &nwk_s_key, 0)?;
assert_eq!(&plaintext, b"test");§Building a downlink
LoraPacket::builder composes a packet field by field. Terminal methods
(sign_and_encrypt, sign_join_request, sign_join_accept) finalise the
MIC and produce wire bytes via LoraPacket::to_wire.
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::Downlink, false)
.dev_addr(DevAddr::new([0x49, 0xbe, 0x7d, 0xf1]))
.f_cnt(2)
.f_port(1)
.payload(b"hello")
.sign_and_encrypt(&app_s_key, &nwk_s_key)?;
let wire: Vec<u8> = packet.to_wire();
assert!(!wire.is_empty());§OTAA session key derivation
use lora_packet::{SessionKeys10, SessionKeys11, AppKey, NwkKey, NetId, AppNonce, DevNonce, AppEui};
let app_key = AppKey::new([0u8; 16]);
let nwk_key = NwkKey::new([0u8; 16]);
let v10 = SessionKeys10::derive(
&app_key,
&NetId::new([0, 0, 0]),
&AppNonce::new([0, 0, 0]),
&DevNonce::new([0, 0]),
);
let v11 = SessionKeys11::derive(
&app_key,
&nwk_key,
&AppEui::new([0u8; 8]),
&AppNonce::new([0, 0, 0]),
&DevNonce::new([0, 0]),
);
let _ = (v10.app_s_key, v11.s_nwk_s_int_key);§API surface at a glance
| Entry point | Purpose |
|---|---|
LoraPacket::from_wire | Parse PHYPayload bytes |
LoraPacket::builder | Compose a packet field by field |
LoraPacket::verify_mic_v1_0 / _v1_1 | Constant-time MIC verification |
LoraPacket::calculate_mic_v1_0 / _v1_1 | MIC computation |
LoraPacket::recalculate_mic_v1_0 / _v1_1 | Overwrite MIC after mutations |
Data::decrypt_payload / encrypt_payload | FRMPayload AES-CTR-style crypt |
Data::decrypt_fopts / encrypt_fopts | 1.1 FOpts MAC-command crypt |
JoinAccept::decrypt_from_wire / encrypt_for_wire | Join Accept on-air decrypt/encrypt |
SessionKeys10::derive / SessionKeys11::derive | OTAA session-key derivation |
JoinServerKeys::derive | 1.1 JS key derivation |
WorKeys::root / WorKeys::session | Relay (WOR) key derivation |
aes_ecb_encrypt | Low-level AES-128 ECB primitive |
§The five message variants
Every LoraPacket holds exactly one Payload variant. Match on it to
pull out type-specific fields:
use lora_packet::{LoraPacket, Payload, RejoinRequest};
let wire = hex::decode("c0000102030405060708090a0b0c0ddeadbeef")?;
let packet = LoraPacket::from_wire(&wire)?;
match &packet.payload {
Payload::JoinRequest(_) => {}
Payload::JoinAccept(_) => {}
Payload::Data(_) => {}
Payload::RejoinRequest(rj) => match rj {
RejoinRequest::Type0 { .. } | RejoinRequest::Type2 { .. } => {}
RejoinRequest::Type1 { .. } => {}
},
Payload::Proprietary(_) => {}
}§Cargo features
| Feature | Default | Effect |
|---|---|---|
std | yes | Enables std::error::Error impls via thiserror/std |
serde | no | Derives Serialize / Deserialize on packet types and keys |
hex_base64 | no | Adds from_hex / from_base64 constructors on keys, ids, packets |
§no_std support
cargo add lora-packet --no-default-featuresThe crate uses alloc::vec::Vec and alloc::string::String; targets must
supply a global allocator. Every public API works identically with or
without the std feature; std only switches Error: std::error::Error
on or off.
§Endianness contract
- Wire format is little-endian (per the
LoRaWANMAC spec). - Struct fields display in network/big-endian order so that printing a
DevAddr([0x49, 0xbe, 0x7d, 0xf1])matches the value used to identify a device on a console.
LoraPacket::from_wire and LoraPacket::to_wire handle the byte
reversal; you only see big-endian fields on the struct.
§Reference specifications
LoRaWAN1.0.4 Specification (TS001-1.0.4).LoRaWAN1.1 Specification (TS001-1.1).LoRaWANRegional ParametersRP002-1.0.4.LoRaAlliance Errata “FCntDwnUsage inFOptsEncryption” (CR v2 r1).
Re-exports§
pub use codec::Data;pub use codec::JoinAccept;pub use codec::JoinRequest;pub use codec::LoraPacket;pub use codec::LoraPacketBuilder;pub use codec::Payload;pub use codec::RejoinRequest;pub use crypto::JoinServerKeys;pub use crypto::SessionKeys10;pub use crypto::SessionKeys11;pub use crypto::WorKeys;pub use crypto::WorSessionKeys;pub use crypto::aes_ecb_encrypt;pub use error::Error;pub use error::Result;pub use mic::V1_0MicKeys;pub use mic::V1_1MicKeys;pub use types::AppEui;pub use types::AppKey;pub use types::AppNonce;pub use types::AppSKey;pub use types::DevAddr;pub use types::DevEui;pub use types::DevNonce;pub use types::Direction;pub use types::DlSettings;pub use types::FCtrl;pub use types::FNwkSIntKey;pub use types::JSEncKey;pub use types::JSIntKey;pub use types::JoinEui;pub use types::JoinNonce;pub use types::LorawanVersion;pub use types::MType;pub use types::Mhdr;pub use types::NetId;pub use types::NwkKey;pub use types::NwkSEncKey;pub use types::NwkSKey;pub use types::RootWorSKey;pub use types::SNwkSIntKey;pub use types::WorSEncKey;pub use types::WorSIntKey;
Modules§
- codec
- Wire-format codec for
LoRaWANpackets. - crypto
- AES-ECB primitives,
FRMPayloadandFOptscrypt, Join Accept crypt, and OTAA / Join Server / WOR key derivation. - error
- Crate-wide error type.
- mic
- CMAC-based message integrity codes for every
LoRaWANmessage type. - types
- Strong typed primitives for
LoRaWANpackets.