Skip to content

Commit

Permalink
Hex Strings (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
lthiery committed Mar 27, 2024
1 parent 3d60594 commit b7d18b2
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 68 deletions.
1 change: 1 addition & 0 deletions lorawan-encoding/Cargo.toml
Expand Up @@ -13,6 +13,7 @@ readme = "README.md"
aes = { version = "0.8", optional = true }
cmac = { version = "0.7", optional = true }
generic-array = "0"
hex = { version = "0", default-features = false }
defmt = { version = "0.3", optional = true }
serde = { version = "1", default-features = false, features = ["derive"], optional = true}

Expand Down
2 changes: 0 additions & 2 deletions lorawan-encoding/src/extra/mod.rs
Expand Up @@ -9,5 +9,3 @@
pub extern crate std;

pub mod hasher;
#[cfg(feature = "with-to-string")]
pub mod to_string;
54 changes: 0 additions & 54 deletions lorawan-encoding/src/extra/to_string.rs

This file was deleted.

105 changes: 93 additions & 12 deletions lorawan-encoding/src/keys.rs
Expand Up @@ -31,22 +31,69 @@ macro_rules! lorawan_key {
}
}

};
}
impl AsRef<[u8]> for $type {
fn as_ref(&self) -> &[u8] {
&self.0 .0
}
}
};
}

lorawan_key!(
/// AppKey should be entered in MSB format. For example, if your LNS provides a AppKey of
/// `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`, you should enter it as `AppKey([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])`.
/// You can construct AppKey from a hex-encoded MSB string or bytes in MSB format.
///
/// Typically, a LNS will provide it in a string format such as: `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`.
///
/// To create from a string:
/// ```
/// use lorawan::keys::AppKey;
/// use core::str::FromStr;
///let appkey = AppKey::from_str("00112233445566778899aabbccddeeff").unwrap();
/// ```
///
/// To create from a byte array, you should enter the bytes in MSB format:
/// ```
/// use lorawan::keys::AppKey;
/// let appkey = AppKey::from([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
/// ```
pub struct AppKey(AES128);
);
lorawan_key!(
/// NwkSKey should be entered in MSB format. For example, if your LNS provides a NwkSKey of
/// `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`, you should enter it as `NwkSKey([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])`.
/// You can construct NewSKey from a hex-encoded MSB string or bytes in MSB format.
///
/// Typically, a LNS will provide a hex-encoded MSB string such as: `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`.
///
/// To create from a string:
/// ```
/// use lorawan::keys::NewSKey;
/// use core::str::FromStr;
/// let newskey = NewSKey::from_str("00112233445566778899aabbccddeeff").unwrap();
/// ```
///
/// To create from a byte array, you should enter the bytes in MSB format:
/// ```
/// use lorawan::keys::NewSKey;
/// let newskey = NewSKey::from([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
/// ```
pub struct NewSKey(AES128);
);
lorawan_key!(
/// AppSKey should be entered in MSB format. For example, if your LNS provides a AppSKey of
/// `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`, you should enter it as `AppSKey([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF])`.
/// You can construct AppSKey from a hex-encoded MSB string or bytes in MSB format.
///
/// Typically, a LNS will provide a hex-encoded MSB string format such as: `00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF`.
///
/// To create from a string:
/// ```
/// use lorawan::keys::AppSKey;
/// use core::str::FromStr;
/// let appskey = AppSKey::from_str("00112233445566778899aabbccddeeff").unwrap();
/// ```
///
/// To create from a byte array, you should enter the bytes in MSB format:
/// ```
/// use lorawan::keys::AppSKey;
/// let appskey = AppSKey::from([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
/// ```
pub struct AppSKey(AES128);
);

Expand All @@ -72,17 +119,51 @@ macro_rules! lorawan_eui {
key.0
}
}

impl AsRef<[u8]> for $type {
fn as_ref(&self) -> &[u8] {
&self.0.as_ref()
}
}
};
}

lorawan_eui!(
/// DevEui should be entered in LSB format. For example, if your LNS provides a DevEui of
/// `00:11:22:33:44:55:66:77`, you should enter it as `DevEui([0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00])`.
/// You can construct DevEui from a hex-encoded MSB string or bytes in LSB format.
///
/// Typically, a LNS will provide a hex-encoded MSB string such as: `00:11:22:33:44:55:66:77`.
///
/// To create from a string:
/// ```
/// use lorawan::keys::DevEui;
/// use core::str::FromStr;
/// let dev_eui = DevEui::from_str("0011223344556677").unwrap();
/// ```
///
/// To create from a byte array, you should enter the bytes in LSB format:
/// ```
/// use lorawan::keys::DevEui;
/// let dev_eui = DevEui::from([0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00]);
/// ```
pub struct DevEui(EUI64<[u8; 8]>);
);
lorawan_eui!(
/// AppEui should be entered in LSB format. For example, if your LNS provides a AppEui of
/// `00:11:22:33:44:55:66:77`, you should enter it as `AppEui([0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00])`.
/// You can construct AppEui from a hex-encoded MSB string or bytes in LSB format.
///
/// Typically, a LNS will provide a hex-encoded MSB string such as: `00:11:22:33:44:55:66:77`.
///
/// To create from a string:
/// ```
/// use lorawan::keys::AppEui;
/// use core::str::FromStr;
/// let app_eui = AppEui::from_str("0011223344556677").unwrap();
/// ```
///
/// To create from a byte array, you should enter the bytes in LSB format:
/// ```
/// use lorawan::keys::AppEui;
/// let app_eui = AppEui::from([0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00]);
/// ```
pub struct AppEui(EUI64<[u8; 8]>);
);

Expand Down
1 change: 1 addition & 0 deletions lorawan-encoding/src/lib.rs
Expand Up @@ -17,6 +17,7 @@ pub mod maccommandcreator;
pub mod maccommands;
pub mod packet_length;
pub mod parser;
pub mod string;

#[cfg(feature = "full")]
pub mod extra;
Expand Down
180 changes: 180 additions & 0 deletions lorawan-encoding/src/string.rs
@@ -0,0 +1,180 @@
use crate::keys::*;
use crate::parser::*;

#[cfg(feature = "with-to-string")]
pub extern crate std;

pub use hex::FromHexError;

macro_rules! fixed_len_struct_impl_to_string_msb {
(
$type:ident,$size:expr;
) => {
impl core::str::FromStr for $type {
type Err = FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0; $size];
hex::decode_to_slice(s.as_bytes(), &mut res)?;
Ok(Self::from(res))
}
}

#[cfg(feature = "with-to-string")]
impl std::string::ToString for $type {
fn to_string(&self) -> std::string::String {
let mut res = std::string::String::with_capacity($size * 2);
res.extend(std::iter::repeat('-').take($size * 2));
let slice = unsafe { &mut res.as_bytes_mut() };
hex::encode_to_slice(self.as_ref(), slice).unwrap();
res
}
}
};
(
$type:ident[$size:expr];
) => {
impl core::str::FromStr for $type<[u8; $size]> {
type Err = FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0; $size];
hex::decode_to_slice(s.as_bytes(), &mut res)?;
Ok(Self::from(res))
}
}

#[cfg(feature = "with-to-string")]
impl<T: AsRef<[u8]>> std::string::ToString for $type<T> {
fn to_string(&self) -> std::string::String {
let mut res = std::string::String::with_capacity($size * 2);
res.extend(std::iter::repeat('-').take($size * 2));
let slice = unsafe { &mut res.as_bytes_mut() };
hex::encode_to_slice(self.as_ref(), slice).unwrap();
res
}
}
};
}

macro_rules! fixed_len_struct_impl_string_lsb {
(
$type:ident,$size:expr;
) => {
impl core::str::FromStr for $type {
type Err = FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut res = [0; $size];
hex::decode_to_slice(s.as_bytes(), &mut res)?;
res.reverse();
Ok(Self::from(res))
}
}

#[cfg(feature = "with-to-string")]
impl std::string::ToString for $type {
fn to_string(&self) -> std::string::String {
let mut res = std::string::String::with_capacity($size * 2);
res.extend(std::iter::repeat('0').take($size * 2));
let slice = unsafe { &mut res.as_bytes_mut() };
self.as_ref().iter().rev().enumerate().for_each(|(i, b)| {
hex::encode_to_slice(&[*b], &mut slice[i * 2..i * 2 + 2]).unwrap();
});
res
}
}
};
}

fixed_len_struct_impl_to_string_msb! {
EUI64[8];
}

fixed_len_struct_impl_to_string_msb! {
DevNonce[2];
}

fixed_len_struct_impl_to_string_msb! {
AppNonce[3];
}

fixed_len_struct_impl_to_string_msb! {
DevAddr[4];
}

fixed_len_struct_impl_to_string_msb! {
NwkAddr[3];
}

fixed_len_struct_impl_to_string_msb! {
AppKey, 16;
}

fixed_len_struct_impl_to_string_msb! {
NewSKey, 16;
}

fixed_len_struct_impl_to_string_msb! {
AppSKey, 16;
}

fixed_len_struct_impl_string_lsb! {
DevEui, 8;
}

fixed_len_struct_impl_string_lsb! {
AppEui, 8;
}

#[cfg(test)]
mod test {
use super::*;
use crate::extra::std::string::ToString;
use core::str::FromStr;

#[test]
fn test_appskey_to_string() {
let appskey = AppSKey::from([
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfd, 0xb9, 0x75, 0x31, 0x24, 0x68,
0xac, 0xed,
]);
assert_eq!(appskey.to_string(), "0123456789abcdeffdb975312468aced");
}

#[test]
fn test_appskey_from_str() {
let appskey = AppSKey::from_str("00112233445566778899aabbccddeeff").unwrap();
assert_eq!(
appskey,
AppSKey::from([
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD,
0xEE, 0xFF
])
);
}

#[test]
fn test_deveui_to_string() {
let deveui = DevEui::from([0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12]);
assert_eq!(deveui.to_string(), "123456789abcdef0");
}

#[test]
fn test_deveui_from_str() {
let deveui = DevEui::from_str("123456789abcdef0").unwrap();
assert_eq!(deveui, DevEui::from([0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12]));
}

#[test]
fn test_deveui_from_small_str() {
let result = DevEui::from_str("123456789abcd");
assert_eq!(result, Err(FromHexError::OddLength));
}

#[test]
fn test_deveui_from_large_str() {
let result = DevEui::from_str("123456789abcdef000");
assert_eq!(result, Err(FromHexError::InvalidStringLength));
}
}

0 comments on commit b7d18b2

Please sign in to comment.