mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-30 17:08:45 +01:00
Fix user-password encryption and decryption
This commit is contained in:
+95
-74
@@ -51,32 +51,35 @@ impl Attribute {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut enc: Vec<u8> = Vec::new();
|
let mut buff = request_authenticator.to_vec();
|
||||||
|
|
||||||
let digest = md5::compute([&secret[..], &request_authenticator[..]].concat());
|
let l = plain_text.len();
|
||||||
enc.extend(digest.to_vec());
|
if l < 16 {
|
||||||
|
let enc = md5::compute([secret, &buff[..]].concat()).to_vec();
|
||||||
let (head, _) = plain_text.split_at(16);
|
return Ok(Attribute(
|
||||||
|
enc.iter()
|
||||||
let mut i = 0;
|
.zip([plain_text, vec![0 as u8; 16 - l].as_slice()].concat())
|
||||||
for b in head {
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ zero padding
|
||||||
enc[i] ^= b;
|
.map(|(d, p)| d ^ p)
|
||||||
i += 1;
|
.collect(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 16;
|
let mut enc: Vec<u8> = Vec::new();
|
||||||
while i < plain_text.len() {
|
for chunk in plain_text.chunks(16) {
|
||||||
let digest = md5::compute([&secret[..], &enc[i - 16..i]].concat());
|
let mut chunk_vec = chunk.to_vec();
|
||||||
enc.extend(digest.to_vec());
|
let l = chunk.len();
|
||||||
|
if l < 16 {
|
||||||
let mut j = 0;
|
chunk_vec.extend(vec![0 as u8; 16 - l]); // zero padding
|
||||||
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;
|
let enc_block = md5::compute([secret, &buff[..]].concat()).to_vec();
|
||||||
|
buff = enc_block
|
||||||
|
.iter()
|
||||||
|
.zip(chunk_vec)
|
||||||
|
.map(|(d, p)| d ^ p)
|
||||||
|
.collect();
|
||||||
|
enc.extend(&buff);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Attribute(enc))
|
Ok(Attribute(enc))
|
||||||
@@ -154,43 +157,28 @@ impl Attribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut dec: Vec<u8> = Vec::new();
|
let mut dec: Vec<u8> = Vec::new();
|
||||||
|
let mut buff: Vec<u8> = request_authenticator.to_vec();
|
||||||
|
|
||||||
let digest = md5::compute([&secret[..], &request_authenticator[..]].concat());
|
// NOTE:
|
||||||
dec.extend(digest.to_vec());
|
// It ensures attribute value has 16 bytes length at least because the value is encoded by md5.
|
||||||
|
// And this must be aligned by each 16 bytes length.
|
||||||
let (head, _) = self.0.split_at(16);
|
for chunk in self.0.chunks(16) {
|
||||||
|
let chunk_vec = chunk.to_vec();
|
||||||
let mut i = 0;
|
let dec_block = md5::compute([secret, &buff[..]].concat()).to_vec();
|
||||||
let mut maybe_first_zero_byte_idx = Option::None;
|
dec.extend(
|
||||||
for b in head {
|
dec_block
|
||||||
dec[i] ^= b;
|
.iter()
|
||||||
if dec[i] == 0 && maybe_first_zero_byte_idx.is_none() {
|
.zip(&chunk_vec)
|
||||||
maybe_first_zero_byte_idx = Option::Some(i)
|
.map(|(d, p)| d ^ p)
|
||||||
}
|
.collect::<Vec<u8>>(),
|
||||||
i += 1;
|
);
|
||||||
|
buff = chunk_vec.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 16;
|
// remove trailing zero bytes
|
||||||
while i < self.0.len() {
|
match dec.split(|b| *b == 0).next() {
|
||||||
let digest = md5::compute([&secret[..], &self.0[i - 16..i]].concat());
|
Some(dec) => Ok(dec.to_vec()),
|
||||||
dec.extend(digest.to_vec());
|
None => Ok(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()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,9 +199,10 @@ mod tests {
|
|||||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
|
|
||||||
use crate::attribute::Attribute;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
|
use crate::attribute::Attribute;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_should_convert_attribute_to_integer32() -> Result<(), String> {
|
fn it_should_convert_attribute_to_integer32() -> Result<(), String> {
|
||||||
assert_eq!(Attribute(vec![1, 2, 3, 4]).to_integer32()?, 16909060);
|
assert_eq!(Attribute(vec![1, 2, 3, 4]).to_integer32()?, 16909060);
|
||||||
@@ -252,26 +241,58 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn it_should_convert_user_password() {
|
fn it_should_convert_user_password() {
|
||||||
let plain_text = b"texttexttexttexttexttexttexttext".to_vec();
|
let secret = b"12345".to_vec();
|
||||||
let secret = b"secret".to_vec();
|
|
||||||
let request_authenticator = b"0123456789abcdef".to_vec();
|
let request_authenticator = b"0123456789abcdef".to_vec();
|
||||||
let user_password_attr_result =
|
|
||||||
Attribute::from_user_password(&plain_text, &secret, &request_authenticator);
|
struct TestCase<'a> {
|
||||||
let user_password_attr = user_password_attr_result.unwrap();
|
plain_text: &'a str,
|
||||||
assert_eq!(
|
expected_encoded_len: usize,
|
||||||
user_password_attr.0,
|
};
|
||||||
vec![
|
|
||||||
0xb7, 0xb0, 0xcb, 0x5d, 0x4f, 0x96, 0xd4, 0x75, 0x1c, 0xea, 0x3a, 0xb6, 0xf, 0xc,
|
let test_cases = &[
|
||||||
0xea, 0xa5, 0xc9, 0x22, 0xac, 0x26, 0x28, 0x23, 0x93, 0xef, 0x19, 0x67, 0xcc, 0xeb,
|
TestCase {
|
||||||
0x9d, 0x33, 0xd7, 0x46
|
plain_text: "",
|
||||||
],
|
expected_encoded_len: 16,
|
||||||
);
|
},
|
||||||
assert_eq!(
|
TestCase {
|
||||||
user_password_attr
|
plain_text: "abc",
|
||||||
|
expected_encoded_len: 16,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
plain_text: "0123456789abcde",
|
||||||
|
expected_encoded_len: 16,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
plain_text: "0123456789abcdef",
|
||||||
|
expected_encoded_len: 16,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
plain_text: "0123456789abcdef0",
|
||||||
|
expected_encoded_len: 32,
|
||||||
|
},
|
||||||
|
TestCase {
|
||||||
|
plain_text: "0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||||
|
expected_encoded_len: 48,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for test_case in test_cases {
|
||||||
|
let user_password_attr_result = Attribute::from_user_password(
|
||||||
|
test_case.plain_text.as_bytes(),
|
||||||
|
&secret,
|
||||||
|
&request_authenticator,
|
||||||
|
);
|
||||||
|
let user_password_attr = user_password_attr_result.unwrap();
|
||||||
|
assert_eq!(user_password_attr.0.len(), test_case.expected_encoded_len);
|
||||||
|
|
||||||
|
let decoded_password = user_password_attr
|
||||||
.to_user_password(&secret, &request_authenticator)
|
.to_user_password(&secret, &request_authenticator)
|
||||||
.unwrap(),
|
.unwrap();
|
||||||
plain_text,
|
assert_eq!(
|
||||||
);
|
String::from_utf8(decoded_password).unwrap(),
|
||||||
|
test_case.plain_text
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user