-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Scaffold EAP Message Authenticator with HMACMD5
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 MessageAuthenticator struct & implementation to create HMACMD5 signature buffers from Packet's encode() byte vector. Implement Message-Authenticator input structure for starting the chain of HMAC exchanges and verifying Access-Request messages. Testing: Implemented test to verify Access-Request message HMACMD5 - pass
- Loading branch information
RageLtMan
committed
Aug 4, 2022
1 parent
7a14eae
commit ae0382b
Showing
5 changed files
with
202 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
use std::fmt; | ||
use hmac::{Hmac, Mac}; | ||
use hmac_md5::Md5; | ||
use crate::core::packet::Packet; | ||
use crate::core::rfc2869; | ||
|
||
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 { | ||
|
||
/// Create a new Message-Authenticator from a 16-byte slice | ||
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 } | ||
} | ||
/// Create a new Message-Authenticator with zeroed-values | ||
pub fn new() -> Self { | ||
MessageAuthenticator { value: [0u8; 16] } | ||
} | ||
/// Create a new Message-Authenticator from input packet | ||
pub fn from_packet(sb: &Packet) -> Self { | ||
let mut mac = HmacMd5::new_from_slice(sb.get_secret()).unwrap(); | ||
mac.update(&sb.encode().expect("Failed to encode packet for hmac-md5")[..]); | ||
Self::from_bytes(&mac.finalize().into_bytes()[..]) | ||
} | ||
/// Create a new Message-Authenticator for an Access-Request RADIUS message. | ||
/// Since this message type is the start of the HMAC-chain, it hashes itself with | ||
/// the hash-input RADIUS message having a Message-Authenticator equivalent to the | ||
/// new() method for this code (all zeroes). | ||
pub fn from_access_request(pkt: &Packet) -> Self { | ||
Self::from_packet( | ||
// zero the existing Message-Authenticator | ||
&Self::new().authenticate_packet(&pkt).unwrap() | ||
) | ||
} | ||
/// Attempt to create new Packet with signature buffer from this MessageAuthenticator's | ||
/// value field. | ||
pub fn authenticate_packet(&self, pkt: &Packet) -> Result<Packet, std::io::Error> { | ||
let mut req_bytes = pkt.encode().unwrap(); | ||
let secret = pkt.get_secret(); | ||
let req_ma_bytes = rfc2869::lookup_message_authenticator(&pkt).unwrap(); | ||
Self::replace_slice(&mut req_bytes, &req_ma_bytes, &self.value[..]); | ||
let res = Packet::decode(&req_bytes, secret).unwrap(); | ||
Ok(res) | ||
} | ||
// From https://stackoverflow.com/questions/54150353/how-to-find-and-replace-every-matching-slice-of-bytes-with-another-slice | ||
fn replace_slice<T>(buf: &mut [T], from: &[T], to: &[T]) | ||
where | ||
T: Clone + PartialEq, | ||
{ | ||
for i in 0..=buf.len() - from.len() { | ||
if buf[i..].starts_with(from) { | ||
buf[i..(i + from.len())].clone_from_slice(to); | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for MessageAuthenticator { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "{:02X?}", self.value) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use hex; | ||
use crate::core::packet::Packet; | ||
use crate::core::message_authenticator::MessageAuthenticator; | ||
use crate::core::rfc2869; | ||
|
||
#[test] | ||
fn it_should_authenticate_access_request() -> Result<(), ()> { | ||
let msg_bytes = hex::decode("01160049b3a5cd2de262bcdbb589752a212e0b2f01067465737404067f00010105060000000150121ca1999b24d5224ddeca96fd7dabac270706000000014f0b023100090174657374").unwrap(); | ||
let secret = b"somesecretval"; | ||
let req_packet = Packet::decode(&msg_bytes, &secret[..]).unwrap(); | ||
assert_eq!(req_packet.encode().unwrap(), msg_bytes); | ||
let ma_bytes = rfc2869::lookup_message_authenticator(&req_packet).unwrap(); | ||
let ma = MessageAuthenticator::from_bytes(&ma_bytes); | ||
let ema = MessageAuthenticator::from_access_request(&req_packet); | ||
|
||
assert_eq!(ma, ema); | ||
Ok(()) | ||
} | ||
#[test] | ||
fn it_should_authenticate_other_access_request() -> Result<(), ()> { | ||
let msg_bytes = hex::decode("013200497d39e88ba3783fc183b54d486c629e8501067465737404067f000101050600000001501208c9801fb78909a8b24ad4dc261b2d470706000000014f0b020200090174657374").unwrap(); | ||
let secret = b"somesecretval"; | ||
let req_packet = Packet::decode(&msg_bytes, &secret[..]).unwrap(); | ||
assert_eq!(req_packet.encode().unwrap(), msg_bytes); | ||
let ma_bytes = rfc2869::lookup_message_authenticator(&req_packet).unwrap(); | ||
let ma = MessageAuthenticator::from_bytes(&ma_bytes); | ||
let ema = MessageAuthenticator::from_access_request(&req_packet); | ||
|
||
assert_eq!(ma, ema); | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,3 +31,4 @@ pub mod rfc7055; | |
pub mod rfc7155; | ||
pub mod tag; | ||
pub mod eap; | ||
pub mod message_authenticator; |