mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-24 22:24:43 +01:00
Add attribute package
This commit is contained in:
93
Cargo.lock
generated
93
Cargo.lock
generated
@@ -1,5 +1,98 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radius-rs"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"md5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
@@ -7,3 +7,5 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
md5 = "0.7.0"
|
||||
chrono = "0.4"
|
||||
|
||||
261
src/attribute.rs
Normal file
261
src/attribute.rs
Normal file
@@ -0,0 +1,261 @@
|
||||
use std::convert::TryInto;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use chrono::{DateTime, Utc, TimeZone};
|
||||
|
||||
pub struct Attribute(Vec<u8>);
|
||||
|
||||
impl Attribute {
|
||||
pub fn from_integer32(v: &u32) -> Self {
|
||||
Attribute(u32::to_be_bytes(*v).to_vec())
|
||||
}
|
||||
|
||||
pub fn from_string(v: &String) -> Self {
|
||||
Attribute(v.as_bytes().to_vec())
|
||||
}
|
||||
|
||||
pub fn from_bytes(v: &Vec<u8>) -> Self {
|
||||
Attribute(v.to_vec())
|
||||
}
|
||||
|
||||
pub fn from_ipv4(v: &Ipv4Addr) -> Self {
|
||||
Attribute(v.octets().to_vec())
|
||||
}
|
||||
|
||||
pub fn from_ipv6(v: &Ipv6Addr) -> Self {
|
||||
Attribute(v.octets().to_vec())
|
||||
}
|
||||
|
||||
pub fn from_user_password(plain_text: &Vec<u8>, secret: &Vec<u8>, request_authenticator: &Vec<u8>) -> Result<Self, String> {
|
||||
if plain_text.len() > 128 {
|
||||
return Err("the length of plain_text has to be within 128, but the given value is longer".to_owned());
|
||||
}
|
||||
|
||||
if secret.len() <= 0 {
|
||||
return Err("secret hasn't be empty, but the given value is empty".to_owned());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err("request_authenticator has to have 16-bytes payload, but the given value doesn't".to_owned());
|
||||
}
|
||||
|
||||
let mut enc: Vec<u8> = Vec::new();
|
||||
|
||||
let digest = md5::compute([&secret[..], &request_authenticator[..]].concat());
|
||||
enc.extend(digest.to_vec());
|
||||
|
||||
let (head, _) = plain_text.split_at(16);
|
||||
|
||||
let mut i = 0;
|
||||
for b in head {
|
||||
enc[i] ^= b;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
i = 16;
|
||||
while i < plain_text.len() {
|
||||
let digest = md5::compute([&secret[..], &enc[i - 16..i]].concat());
|
||||
enc.extend(digest.to_vec());
|
||||
|
||||
let mut j = 0;
|
||||
for b in &plain_text[i..i + 16] { // TODO this has to be 16 bounds, is this correct?
|
||||
enc[i + j] ^= b;
|
||||
j += 1;
|
||||
}
|
||||
|
||||
i += 16;
|
||||
}
|
||||
|
||||
Ok(Attribute(enc))
|
||||
}
|
||||
|
||||
pub fn from_date(dt: &DateTime<Utc>) -> Self {
|
||||
Attribute(u32::to_be_bytes(dt.timestamp() as u32).to_vec())
|
||||
}
|
||||
|
||||
pub fn to_integer32(&self) -> Result<u32, String> {
|
||||
const EXPECTED_SIZE: usize = std::mem::size_of::<u32>();
|
||||
if self.0.len() != EXPECTED_SIZE {
|
||||
return Err("invalid attribute length for integer".to_owned());
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.0.split_at(EXPECTED_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => Ok(u32::from_be_bytes(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> Result<String, FromUtf8Error> {
|
||||
String::from_utf8(self.0.to_vec())
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_vec()
|
||||
}
|
||||
|
||||
pub fn to_ipv4(&self) -> Result<Ipv4Addr, String> {
|
||||
const IPV4_SIZE: usize = std::mem::size_of::<Ipv4Addr>();
|
||||
if self.0.len() != IPV4_SIZE {
|
||||
return Err("invalid attribute length for ipv4 address".to_owned());
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.0.split_at(IPV4_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok::<[u8; IPV4_SIZE], _>(boxed_array) => Ok(Ipv4Addr::from(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ipv6(&self) -> Result<Ipv6Addr, String> {
|
||||
const IPV6_SIZE: usize = std::mem::size_of::<Ipv6Addr>();
|
||||
if self.0.len() != IPV6_SIZE {
|
||||
return Err("invalid attribute length for ipv6 address".to_owned());
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.0.split_at(IPV6_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok::<[u8; IPV6_SIZE], _>(boxed_array) => Ok(Ipv6Addr::from(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_user_password(&self, secret: &Vec<u8>, request_authenticator: &Vec<u8>) -> Result<Vec<u8>, String> {
|
||||
if self.0.len() < 16 || self.0.len() > 128 {
|
||||
return Err(format!("invalid attribute length {}", self.0.len()));
|
||||
}
|
||||
|
||||
if secret.len() <= 0 {
|
||||
return Err("secret hasn't be empty, but the given value is empty".to_owned());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err("request_authenticator has to have 16-bytes payload, but the given value doesn't".to_owned());
|
||||
}
|
||||
|
||||
let mut dec: Vec<u8> = Vec::new();
|
||||
|
||||
let digest = md5::compute([&secret[..], &request_authenticator[..]].concat());
|
||||
dec.extend(digest.to_vec());
|
||||
|
||||
let (head, _) = self.0.split_at(16);
|
||||
|
||||
let mut i = 0;
|
||||
let mut maybe_first_zero_byte_idx = Option::None;
|
||||
for b in head {
|
||||
dec[i] ^= b;
|
||||
if dec[i] == 0 && maybe_first_zero_byte_idx.is_none() {
|
||||
maybe_first_zero_byte_idx = Option::Some(i)
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
i = 16;
|
||||
while i < self.0.len() {
|
||||
let digest = md5::compute([&secret[..], &self.0[i - 16..i]].concat());
|
||||
dec.extend(digest.to_vec());
|
||||
|
||||
let mut j = 0;
|
||||
for b in &self.0[i..i + 16] { // TODO this has to be 16 bounds, is this correct?
|
||||
dec[i + j] ^= b;
|
||||
if dec[i + j] == 0 && maybe_first_zero_byte_idx.is_none() {
|
||||
maybe_first_zero_byte_idx = Option::Some(i + j)
|
||||
}
|
||||
j += 1;
|
||||
}
|
||||
|
||||
i += 16;
|
||||
}
|
||||
|
||||
match maybe_first_zero_byte_idx {
|
||||
None => Ok(dec),
|
||||
Some(idx) => Ok(dec[..idx].to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_date(&self) -> Result<DateTime<Utc>, String> {
|
||||
let (int_bytes, _) = self.0.split_at(std::mem::size_of::<u32>());
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => {
|
||||
let timestamp = u32::from_be_bytes(boxed_array);
|
||||
Ok(Utc.timestamp(timestamp as i64, 0))
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use crate::attribute::Attribute;
|
||||
use chrono::Utc;
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_attribute_to_integer32() -> Result<(), String> {
|
||||
assert_eq!(Attribute(vec![1, 2, 3, 4]).to_integer32()?, 16909060);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_attribute_to_string() -> Result<(), FromUtf8Error> {
|
||||
assert_eq!(
|
||||
Attribute(vec![0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64]).to_string()?,
|
||||
"Hello, World"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_ipv4() -> Result<(), String> {
|
||||
let given_ipv4 = Ipv4Addr::new(192, 0, 2, 1);
|
||||
let ipv4_attr = Attribute::from_ipv4(&given_ipv4);
|
||||
assert_eq!(
|
||||
ipv4_attr.to_ipv4()?,
|
||||
given_ipv4,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_ipv6() -> Result<(), String> {
|
||||
let given_ipv6 = Ipv6Addr::new(0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001);
|
||||
let ipv6_attr = Attribute::from_ipv6(&given_ipv6);
|
||||
assert_eq!(
|
||||
ipv6_attr.to_ipv6()?,
|
||||
given_ipv6,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_user_password() {
|
||||
let plain_text = b"texttexttexttexttexttexttexttext".to_vec();
|
||||
let secret = b"secret".to_vec();
|
||||
let request_authenticator = b"0123456789abcdef".to_vec();
|
||||
let user_password_attr_result = Attribute::from_user_password(&plain_text, &secret, &request_authenticator);
|
||||
let user_password_attr = user_password_attr_result.unwrap();
|
||||
assert_eq!(
|
||||
user_password_attr.0,
|
||||
vec![0xb7, 0xb0, 0xcb, 0x5d, 0x4f, 0x96, 0xd4, 0x75, 0x1c, 0xea, 0x3a, 0xb6, 0xf, 0xc, 0xea, 0xa5, 0xc9, 0x22, 0xac, 0x26, 0x28, 0x23, 0x93, 0xef, 0x19, 0x67, 0xcc, 0xeb, 0x9d, 0x33, 0xd7, 0x46],
|
||||
);
|
||||
assert_eq!(
|
||||
user_password_attr.to_user_password(&secret, &request_authenticator).unwrap(),
|
||||
plain_text,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_date() -> Result<(), String> {
|
||||
let now = Utc::now();
|
||||
let attr = Attribute::from_date(&now);
|
||||
assert_eq!(
|
||||
attr.to_date()?.timestamp(),
|
||||
now.timestamp(),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
pub mod code;
|
||||
pub mod attribute;
|
||||
|
||||
Reference in New Issue
Block a user