Fix user-password encryption and decryption

This commit is contained in:
moznion
2020-11-28 04:03:26 +09:00
parent dbe8a9f226
commit f73a22ad49
+95 -74
View File
@@ -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]