pamoja_lorawan/lib.rs
1#![cfg_attr(not(test), no_std)]
2
3//! LoRaWAN 1.0.x MAC framing for the pamoja SDK.
4//!
5//! LoRaWAN is how a low-power node reaches a network kilometers away over a license-free
6//! radio, which is why it is the SDK's first-class answer for rural and remote reach. The
7//! [`pamoja-lora`](https://docs.rs/pamoja-lora) crate gives the link budget, the exact
8//! time a transmission spends on air; this crate gives the bytes that go in it: the
9//! secured LoRaWAN frame.
10//!
11//! A LoRaWAN frame is not just a payload with an address. The standard wraps every frame
12//! in two cryptographic guarantees, because a long-range public-band link is wide open: a
13//! message integrity code keyed to the network proves the frame is authentic and intact,
14//! and the payload is encrypted to the application so only its owner can read it. This
15//! crate builds and verifies exactly that, with no radio and no allocation:
16//!
17//! - [`Session`] - an activated device's address and session keys. It [encodes](Session::encode_uplink)
18//! an uplink or downlink data frame, encrypting the payload and appending the MIC, and
19//! [decodes](Session::decode) one received, verifying the MIC before decrypting.
20//! - [`Uplink`] and [`Downlink`] - the data frame to send, built up from the fields a
21//! sender sets (confirmed, adaptive data rate, acknowledgment, frame options).
22//! - [`RxData`] - a decoded frame: its header fields and its recovered payload.
23//! - [`Device`] - the root credentials for over-the-air activation: it builds the
24//! join-request a device broadcasts and turns the network's join-accept into a ready
25//! [`Session`], deriving the session keys the spec prescribes.
26//! - [`JoinRequest`] and [`JoinGrant`] - the other half of that exchange, so a deployment
27//! can run its own network instead of joining someone else's: verify the request a
28//! device sent, then grant it an address and sign the reply. Both sides derive the same
29//! session keys from the same nonces, with no key ever on the air.
30//! - [`FrameHeader`] - what a frame says about itself before any key is involved: its
31//! message type, the device address, and the counter. A receiver holding many sessions
32//! reads this first to find the one a frame belongs to, then decodes.
33//! - [`CfList`] - the channel list a join accept may end with, in either of the two forms the
34//! regional parameters define.
35//! - [`defaults`] - the receive delays, frame counter gap and ADR counts every region
36//! shares, and [`adr`] and [`mac`] for keeping a device reachable and configuring it.
37//! - [`device`] - a Class A end device with no radio: it joins, picks a channel and data rate
38//! for each uplink, says when to listen, reads what the network sends back and does what
39//! its MAC commands ask, following the [`Version`] of the link layer it is given.
40//!
41//! The cryptography is the LoRaWAN construction over AES-128: an AES-CMAC MIC and an
42//! AES keystream for the payload, with the device address and frame counter folded into
43//! both so a frame cannot be lifted out of its place in the stream. The radio itself is
44//! `pamoja-radios`' part, so everything here runs the same on a microcontroller, on a
45//! gateway, and in a test with no hardware at all.
46//!
47//! # Examples
48//!
49//! ```
50//! use pamoja_lorawan::{Session, Uplink};
51//!
52//! // A node activated with a device address and its two session keys.
53//! let session = Session::new(0x2601_1BDA, [0x2B; 16], [0x99; 16]);
54//!
55//! // Encode a confirmed uplink reading; the payload is encrypted and the MIC appended.
56//! let frame = session
57//! .encode_uplink(&Uplink::new(42, 1, b"temp=4.8").confirmed())
58//! .unwrap();
59//!
60//! // The network, holding the same session, verifies and decrypts it.
61//! let rx = session.decode(frame.as_bytes(), 42).unwrap();
62//! assert!(rx.confirmed());
63//! assert_eq!(rx.payload(), b"temp=4.8");
64//! ```
65
66pub mod adr;
67mod cflist;
68mod crypto;
69pub mod defaults;
70pub mod device;
71mod error;
72mod frame;
73mod header;
74mod hex;
75mod join;
76pub mod mac;
77mod network;
78pub mod packages;
79pub mod relay;
80mod session;
81mod version;
82
83pub use adr::Backoff;
84pub use cflist::{CfList, CfListKind, CFLIST_FREQUENCIES, CFLIST_LEN, CFLIST_MASK_GROUPS};
85pub use error::LorawanError;
86pub use frame::{Direction, PhyPayload, MAX_FRAME, MAX_PAYLOAD};
87pub use header::{FrameHeader, MessageType};
88pub use hex::parse_hex;
89pub use join::{Device, JoinAccept};
90pub use mac::{MacCommand, MacCommands};
91pub use network::{JoinGrant, JoinRequest};
92pub use session::{Downlink, RxData, Session, Uplink};
93pub use version::Version;