lorawan_device/mac/
otaa.rs

1use super::{del_to_delay_ms, session::Session, Response};
2use crate::radio::RadioBuffer;
3use crate::region::Configuration;
4use crate::{AppEui, AppKey, DevEui};
5use lorawan::keys::CryptoFactory;
6use lorawan::{
7    creator::JoinRequestCreator,
8    parser::{parse_with_factory as lorawan_parse, *},
9};
10use rand_core::RngCore;
11
12pub(crate) type DevNonce = lorawan::parser::DevNonce<[u8; 2]>;
13
14pub(crate) struct Otaa {
15    dev_nonce: DevNonce,
16    network_credentials: NetworkCredentials,
17}
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19#[derive(Debug, Clone)]
20pub struct NetworkCredentials {
21    deveui: DevEui,
22    appeui: AppEui,
23    appkey: AppKey,
24}
25
26impl Otaa {
27    pub fn new(network_credentials: NetworkCredentials) -> Self {
28        Self { dev_nonce: DevNonce::from([0, 0]), network_credentials }
29    }
30
31    /// Prepare a join request to be sent. This populates the radio buffer with the request to be
32    /// sent, and returns the radio config to use for transmitting.
33    pub(crate) fn prepare_buffer<C: CryptoFactory + Default, G: RngCore, const N: usize>(
34        &mut self,
35        rng: &mut G,
36        buf: &mut RadioBuffer<N>,
37    ) -> u16 {
38        self.dev_nonce = DevNonce::from(rng.next_u32() as u16);
39        buf.clear();
40        let mut phy: JoinRequestCreator<[u8; 23], C> = JoinRequestCreator::default();
41        phy.set_app_eui(self.network_credentials.appeui)
42            .set_dev_eui(self.network_credentials.deveui)
43            .set_dev_nonce(self.dev_nonce);
44        let vec = phy.build(&self.network_credentials.appkey);
45        buf.extend_from_slice(vec).unwrap();
46        u16::from(self.dev_nonce)
47    }
48
49    pub(crate) fn handle_rx<C: CryptoFactory + Default, const N: usize>(
50        &mut self,
51        region: &mut Configuration,
52        configuration: &mut super::Configuration,
53        rx: &mut RadioBuffer<N>,
54    ) -> Option<Session> {
55        if let Ok(PhyPayload::JoinAccept(JoinAcceptPayload::Encrypted(encrypted))) =
56            lorawan_parse(rx.as_mut_for_read(), C::default())
57        {
58            let decrypt = encrypted.decrypt(&self.network_credentials.appkey);
59            region.process_join_accept(&decrypt);
60            configuration.rx1_delay = del_to_delay_ms(decrypt.rx_delay());
61            if decrypt.validate_mic(&self.network_credentials.appkey) {
62                return Some(Session::derive_new(
63                    &decrypt,
64                    self.dev_nonce,
65                    &self.network_credentials,
66                ));
67            }
68        }
69        None
70    }
71
72    pub(crate) fn rx2_complete(&mut self) -> Response {
73        Response::NoJoinAccept
74    }
75}
76
77impl NetworkCredentials {
78    pub fn new(appeui: AppEui, deveui: DevEui, appkey: AppKey) -> Self {
79        Self { deveui, appeui, appkey }
80    }
81    pub fn appeui(&self) -> &AppEui {
82        &self.appeui
83    }
84
85    pub fn deveui(&self) -> &DevEui {
86        &self.deveui
87    }
88
89    pub fn appkey(&self) -> &AppKey {
90        &self.appkey
91    }
92}