Skip to content

Commit

Permalink
Scaffold EAP Message Authenticator with HMACMD5
Browse files Browse the repository at this point in the history
Implement a new type using the hmac and md-5 crates, namespacing
md-5 as hmac_md5 to avoid collision with existing crate.

Stub out authenticator implementation for handling an auth request.

Testing:
  Failing tests because i'm either using the wrong inputs, hash,
or a mix of the two.
  • Loading branch information
RageLtMan committed Jul 31, 2022
1 parent 7a14eae commit b57ed86
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 8 deletions.
15 changes: 9 additions & 6 deletions examples/eap_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tokio::signal;
use radius::core::code::Code;
use radius::core::packet::Packet;
use radius::core::request::Request;
use radius::core::eap::{EAP, EAPType, EAPCode};
use radius::core::eap::{EAP, EAPType, EAPCode, MessageAuthenticator};
use radius::core::rfc2865;
use radius::core::rfc2869;
use radius::server::{RequestHandler, SecretProvider, SecretProviderError, Server};
Expand Down Expand Up @@ -105,7 +105,7 @@ impl RequestHandler<(), io::Error> for MyRequestHandler {
info!("maybe usr pass looked up");
let maybe_eap_message = rfc2869::lookup_eap_message(req_packet);
info!("maybe eap looked up");
match maybe_eap_message {
let eap_message = match maybe_eap_message {
Some(e) => {
let ieap = EAP::from_bytes(&e);
println!("EAP Message: {}", &ieap);
Expand All @@ -120,14 +120,17 @@ impl RequestHandler<(), io::Error> for MyRequestHandler {
};
let maybe_message_authenticator = rfc2869::lookup_message_authenticator(req_packet);
let message_authenticator = match maybe_message_authenticator {
Some(m) => m.to_vec(),
Some(m) => MessageAuthenticator::from_bytes(&m),
None => {
println!("No authenticator found");
vec![0u8;16]
MessageAuthenticator::new()
}
};
println!("Message Authenticator: {:#?}", &message_authenticator);

println!("Message Authenticator: {}", &message_authenticator);
let remote_addr: SocketAddr = "127.0.0.1:1812".parse().unwrap();
let secret = self.secret_provider.fetch_secret(remote_addr).unwrap();
let our_authenticator = MessageAuthenticator::for_access_request(eap_message, &secret);
println!("Our Authenticator: {}", &our_authenticator);
let user_name = maybe_user_name_attr.unwrap().unwrap();
let user_password = match maybe_user_password_attr {
Some(e) => match e {
Expand Down
2 changes: 2 additions & 0 deletions radius/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ thiserror = "1.0"
log = "0.4.14"
tokio = { version = "1.6.1", features = ["full"] }
async-trait = "0.1.50"
hmac = "0.12"
hmac_md5 = { package = "md-5", version = "0.10"}

[dev-dependencies]
hex = "0.4"
118 changes: 116 additions & 2 deletions radius/src/core/eap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,99 @@
use std::fmt;
use std::convert::TryFrom;
use num_enum::TryFromPrimitive;
use hmac::{Hmac, Mac};
use hmac_md5::Md5;
use crate::core::packet::Packet;

type HmacMd5 = Hmac<Md5>;

///!
// A RADIUS client receiving an Access-Accept, Access-Reject or
// Access-Challenge with a Message-Authenticator attribute present
// MUST calculate the correct value of the Message-Authenticator and
// silently discard the packet if it does not match the value sent.
// This attribute is not required in Access-Requests which include
// the User-Password attribute, but is useful for preventing attacks
// on other types of authentication. This attribute is intended to
// thwart attempts by an attacker to setup a "rogue" NAS, and perform
// online dictionary attacks against the RADIUS server. It does not
// afford protection against "offline" attacks where the attacker
// intercepts packets containing (for example) CHAP challenge and
// response, and performs a dictionary attack against those packets
// offline.
// A summary of the Message-Authenticator attribute format is shown
// below. The fields are transmitted from left to right.
// 0 1 2
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Length | String...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Type
// 80 for Message-Authenticator
// Length
// 18
// String
// When present in an Access-Request packet, Message-Authenticator is
// an HMAC-MD5 [RFC2104] hash of the entire Access-Request packet,
// including Type, ID, Length and Authenticator, using the shared
// secret as the key, as follows.
// Message-Authenticator = HMAC-MD5 (Type, Identifier, Length,
// Request Authenticator, Attributes)
// When the message integrity check is calculated the signature
// string should be considered to be sixteen octets of zero.
// For Access-Challenge, Access-Accept, and Access-Reject packets,
// the Message-Authenticator is calculated as follows, using the
// Request-Authenticator from the Access-Request this packet is in
// reply to:
// Message-Authenticator = HMAC-MD5 (Type, Identifier, Length,
// Request Authenticator, Attributes)
// When the message integrity check is calculated the signature
// string should be considered to be sixteen octets of zero. The
// shared secret is used as the key for the HMAC-MD5 message
// integrity check. The Message-Authenticator is calculated and
// inserted in the packet before the Response Authenticator is
// calculated.
///!
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct MessageAuthenticator {
pub value: [u8; 16]
}

impl MessageAuthenticator {
pub fn from_bytes(bytes: &[u8]) -> Self {
assert_eq!(bytes.len(), 16);
let mut arr = [0u8; 16];
for (place, element) in arr.iter_mut().zip(bytes.iter()) {
*place = *element;
}
MessageAuthenticator { value: arr }
}

pub fn new() -> Self {
MessageAuthenticator { value: [0u8; 16] }
}


///! From Erlang implementation of eradius: apps/eradius/src/eradius_decode.erl
// 40 BlankedAttrBin=zeroAttr(AttrBin, <<80, 18, MsgAuth/binary>>),
// 41 CalculatedAuth=crypto:hmac(md5, NASSecret,
// 42 <<Type/binary, Identifier/binary, Len/binary,
// 43 ReqAuth/binary, BlankedAttrBin/binary>>),

pub fn for_access_request(ar: EAP, secret: &[u8]) -> Self {
let mut mac = HmacMd5::new_from_slice(secret).unwrap();
let mut sum_buf = Vec::<u8>::new();
sum_buf.push(ar.typ as u8); // Type
sum_buf.push(ar.id); // Identifier
sum_buf.extend(EAP::len_to_bytes(ar.len)); // Len
sum_buf.extend(ar.data.clone()); // The auth request in the EAP packet?
sum_buf.extend(&Self::new().value[..]); // Blank auth request
mac.update(&sum_buf[..]);
let hash = mac.finalize();
Self::from_bytes(&hash.into_bytes()[..])
}
}

///! From https://datatracker.ietf.org/doc/html/rfc2284
///
Expand Down Expand Up @@ -170,6 +263,7 @@ impl EAP {
(5 + self.data.len()) as u16
}
/// Create a response message of the requested type from current state
/// Format the raw pair of bytes in an EAP message buffer into an appropriate u16
fn len_from_bytes(bytes: &[u8]) -> u16 {
((bytes[0] as u16) << 8) | bytes[1] as u16
}
Expand All @@ -179,6 +273,12 @@ impl EAP {
}
}

impl fmt::Display for MessageAuthenticator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:02X?}", self.value)
}
}

impl fmt::Display for EAP {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
Expand All @@ -193,6 +293,7 @@ impl fmt::Display for EAP {
mod tests {
use hex;
use crate::core::eap::*;
use crate::core::packet::Packet;

#[test]
fn it_should_decode_eap_id() -> Result<(), ()> {
Expand Down Expand Up @@ -222,11 +323,24 @@ mod tests {
assert_eq!("test".to_owned(), String::from_utf8(eap.data).unwrap());
Ok(())
}

#[test]
fn it_should_marshal_eap_correctly() -> Result<(),()> {
let eap_bytes = hex::decode("027200090174657374").unwrap();
let eap = EAP::from_bytes(&eap_bytes[..]);
assert_eq!(eap_bytes, eap.to_bytes());
Ok(())
}
}
#[test]
fn it_should_authenticate_message() -> Result<(), ()> {
// let msg_bytes = hex::decode("017100430013121fa3f9f5204307789dfdfd7db901067465737404067f00010105060000000150122ddc22247de7db64c9f50d75524e52744f0b027200090174657374").unwrap();
let eap_bytes = hex::decode("027200090174657374").unwrap();
let authenticator_bytes = hex::decode("2ddc22247de7db64c9f50d75524e5274").unwrap();
let secret = "somesecretval";
let eap = EAP::from_bytes(&eap_bytes[..]);
// let radius = Packet::decode(&msg_bytes[..], &secret.as_bytes()).unwrap();
let ma = MessageAuthenticator::from_bytes(&authenticator_bytes[..]);
let ema = MessageAuthenticator::for_access_request(eap, &secret.as_bytes());
assert_eq!(ma, ema);
Ok(())
}
}

0 comments on commit b57ed86

Please sign in to comment.