Skip to main content

pamoja_lorawan/relay/
keys.rs

1//! The keys a relay and an end device protect wake-on-radio frames with, TS011-1.0.1
2//! section 4.
3
4use crate::crypto::Cipher;
5
6/// Derives the root relay session key of an end device, TS011-1.0.1 section 4.4.
7///
8/// `RootWorSKey = aes128_encrypt(Key, 0x01 | pad16)`, where the key is the network session
9/// key: `NwkSKey` under LoRaWAN 1.0.x and `NwkSEncKey` from 1.1. An over-the-air device
10/// derives it after each join, and a network sends it to a relay in `UpdateUplinkListReq`.
11///
12/// # Arguments
13///
14/// * `network_key` - the end device's network session key.
15///
16/// # Returns
17///
18/// The root relay session key.
19///
20/// # Examples
21///
22/// ```
23/// use pamoja_lorawan::relay::root_wor_s_key;
24///
25/// // The vector The Things Stack tests its derivation with.
26/// let nwk_s_enc_key = [
27///     0xCE, 0x07, 0xA0, 0x09, 0xA3, 0x97, 0x0A, 0xC0, 0x51, 0x9A, 0x09, 0x9E, 0xD5, 0x3E, 0x55,
28///     0x0B,
29/// ];
30/// assert_eq!(
31///     root_wor_s_key(&nwk_s_enc_key),
32///     [
33///         0xEE, 0x91, 0xDC, 0x1A, 0x66, 0x66, 0xC0, 0x6E, 0x82, 0x77, 0xDE, 0x6D, 0xB4, 0xDB,
34///         0x94, 0x5F
35///     ]
36/// );
37/// ```
38#[must_use]
39pub fn root_wor_s_key(network_key: &[u8; 16]) -> [u8; 16] {
40    let mut block = [0u8; 16];
41    block[0] = 0x01;
42    Cipher::new(network_key).encrypt_block(&block)
43}
44
45/// The integrity and encryption keys of one end device's wake-on-radio frames.
46#[derive(Clone, Copy, PartialEq, Eq)]
47pub struct WorKeys {
48    integrity: [u8; 16],
49    encryption: [u8; 16],
50}
51
52impl WorKeys {
53    /// Derives an end device's keys from its root relay session key, TS011-1.0.1
54    /// section 4.5.
55    ///
56    /// `WorSIntKey = aes128_encrypt(RootWorSKey, 0x01 | DevAddr | pad16)` and
57    /// `WorSEncKey = aes128_encrypt(RootWorSKey, 0x02 | DevAddr | pad16)`.
58    ///
59    /// # Arguments
60    ///
61    /// * `root_wor_s_key` - the device's root relay session key.
62    /// * `dev_addr` - the device's address.
63    ///
64    /// # Returns
65    ///
66    /// The two keys.
67    #[must_use]
68    pub fn derive(root_wor_s_key: &[u8; 16], dev_addr: u32) -> WorKeys {
69        let cipher = Cipher::new(root_wor_s_key);
70        let mut block = [0u8; 16];
71        block[1..5].copy_from_slice(&dev_addr.to_le_bytes());
72        block[0] = 0x01;
73        let integrity = cipher.encrypt_block(&block);
74        block[0] = 0x02;
75        let encryption = cipher.encrypt_block(&block);
76        WorKeys {
77            integrity,
78            encryption,
79        }
80    }
81
82    /// Holds keys derived earlier, such as ones a relay keeps for an end device it trusts.
83    ///
84    /// # Arguments
85    ///
86    /// * `integrity` - `WorSIntKey`.
87    /// * `encryption` - `WorSEncKey`.
88    ///
89    /// # Returns
90    ///
91    /// The keys.
92    #[must_use]
93    pub const fn new(integrity: [u8; 16], encryption: [u8; 16]) -> WorKeys {
94        WorKeys {
95            integrity,
96            encryption,
97        }
98    }
99
100    /// Returns the key a WOR frame's and a WOR ACK's integrity code is computed with,
101    /// `WorSIntKey`.
102    ///
103    /// # Returns
104    ///
105    /// The key.
106    #[must_use]
107    pub const fn integrity(&self) -> &[u8; 16] {
108        &self.integrity
109    }
110
111    /// Returns the key their payloads are encrypted with, `WorSEncKey`.
112    ///
113    /// # Returns
114    ///
115    /// The key.
116    #[must_use]
117    pub const fn encryption(&self) -> &[u8; 16] {
118        &self.encryption
119    }
120}
121
122impl core::fmt::Debug for WorKeys {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        f.write_str("WorKeys { .. }")
125    }
126}