/// RFC 2865/2866 request and response authenticator calculations.
///|
fn packet_without_authenticator(
packet : Packet,
authenticator : Bytes,
) -> Bytes raise RadiusError {
let body = encode_attributes(packet.attributes)
let total = 20 + body.length()
if total > 65535 {
raise InvalidLength(total)
}
join_bytes(
bytes_from_array([
byte_at(packet.header.code.to_int()),
packet.header.identifier,
byte_at(total >> 8),
byte_at(total),
]),
join_bytes(authenticator, body),
)
}
///|
/// Compute the response authenticator for Access-Accept/Reject/Challenge or
/// Accounting-Response packets.
pub fn response_authenticator(
packet : Packet,
request_authenticator : Bytes,
secret : Bytes,
) -> Bytes raise RadiusError {
if request_authenticator.length() != 16 {
raise InvalidAuthenticator
}
md5_bytes(
join_bytes(
packet_without_authenticator(packet, request_authenticator),
secret,
),
)
}
///|
/// Attach a calculated response authenticator to a packet.
pub fn Packet::with_response_authenticator(
self : Packet,
request_authenticator : Bytes,
secret : Bytes,
) -> Packet raise RadiusError {
let authenticator = response_authenticator(
self, request_authenticator, secret,
)
Packet::new(self.header.code, self.header.identifier, authenticator).with_attributes(
self.attributes,
)
}
///|
/// Add several attributes in one operation.
pub fn Packet::with_attributes(
self : Packet,
attributes : Array[Attribute],
) -> Packet {
for attribute in attributes {
self.attributes.push(attribute)
}
self
}
///|
/// Verify that a received response carries the expected authenticator.
pub fn verify_response_authenticator(
packet : Packet,
request_authenticator : Bytes,
secret : Bytes,
) -> Bool raise RadiusError {
if packet.header.code != AccessAccept &&
packet.header.code != AccessReject &&
packet.header.code != AccessChallenge &&
packet.header.code != AccountingResponse {
false
} else {
byte_eq(
packet.header.authenticator,
response_authenticator(packet, request_authenticator, secret),
)
}
}
///|
/// Validate the packet type expected by an Access response workflow.
pub fn validate_access_response(packet : Packet) -> Bool {
packet.header.code == AccessAccept ||
packet.header.code == AccessReject ||
packet.header.code == AccessChallenge
}
///|
/// Validate the packet type expected by an Accounting response workflow.
pub fn validate_accounting_response(packet : Packet) -> Bool {
packet.header.code == AccountingResponse
}