Sign in

    moonradius

    A transport-independent RADIUS packet and attribute toolkit for MoonBit.

    radius
    aaa
    network
    authentication
    accounting
    Download zip
    Author
    Version
    0.1.1
    License
    Apache-2.0
    Last updated
    24 days ago
    Downloads
    16

    #MoonRADIUS

    MoonRADIUS 是一个面向 MoonBit 的 RADIUS/AAA 协议基础库。它把报文边界、属性 TLV、PAP/CHAP、响应认证器、Accounting 生命周期和可替换传输边界放在一个可测试的纯 MoonBit 包中,适合 NAS、企业 Wi-Fi、VPN 网关、协议测试器和离线报文工具使用。

    它不是 FreeRADIUS 的替代品:库不管理用户数据库,不实现 EAP 方法、RadSec/TLS 或 LDAP/Kerberos/OAuth 身份后端。应用负责 UDP/socket,MoonRADIUS 负责协议数据和确定性的校验结果。

    #安装

    需要 MoonBit 工具链版本不低于 0.10.14(本项目 CI 使用 MoonBit CLI 的稳定版本)。

    moon add shangwuxi/moonradius@0.1.1

    在代码中导入:

    import {
    "shangwuxi/moonradius" @radius,
    }

    #已实现能力

    • RFC 2865/2866 报文头和属性 TLV 的有界编码、解码;
    • Access-Request/Accept/Reject/Challenge 与 Accounting-Request/Response;
    • User-Password 分块加密/解密、CHAP 响应计算、响应认证器;
    • 标准属性常量、整数/文本/IPv4 构造器、Vendor-Specific 属性;
    • 字典查询、重复属性保留、未知属性保留、稳定结构诊断;
    • PAP/CHAP 访问策略、Accounting 记录抽取、内存 DatagramTransport;
    • 重传/冲突分类和 identifier/request-authenticator 关联;
    • 31 个白盒/黑盒测试,包含正例、截断、非法长度、认证和完整工作流。

    #最小可运行示例

    仓库中的 examples/minimal 是一个可直接执行的最小程序:构造 Wi-Fi Access-Request,编码后解码,再输出报文类型、长度和诊断数量。

    moon run examples/minimal

    预期输出:

    code=Access-Request identifier=42 wire_bytes=97 diagnostics=0

    #三个库内工作流示例

    #1. 企业 Wi-Fi PAP

    example_wifi_request() 构造带 User-Name、加密 User-Password、NAS-Identifier 和 NAS-Port 的 Access-Request;example_wifi_accept() 使用内存策略校验后生成带响应认证器的 Access-Accept。

    #2. VPN CHAP Challenge

    example_vpn_challenge() 生成带 CHAP-Password 和 State 的请求。应用可以把 AccessDecision::Challenge 作为下一阶段挑战响应,而无需把 VPN 业务逻辑塞入协议编码器。

    #3. NAS Accounting Start/Stop

    example_accounting_start() 生成 Accounting-Start;make_accounting_stop() 附加输入/输出字节并生成 Stop。accounting_response() 校验必需属性后签名 Accounting-Response。

    #最小库用法

    let request = @radius.Packet::request(@radius.AccessRequest, 1)
    .with_attribute(@radius.Attribute::text(@radius.User_Name, "alice"))
    let wire = @radius.encode(request)
    let parsed = @radius.decode(wire)
    let diagnostics = @radius.diagnose(wire)

    #验证

    moon fmt --check moon check --deny-warn moon build --target wasm-gc --deny-warn moon test --target wasm-gc moon run examples/minimal

    GitHub Actions 会在干净环境中安装 MoonBit、解析依赖,并执行格式检查、静态检查、构建和测试。

    #协议边界与参考

    项目以 RFC 2865、RFC 2866 为协议依据,参考 Go 项目 layeh/radius 的公开行为和 API 组织方式,但 MoonBit 实现为独立重写,不复制上游源码。上游为 MPL-2.0;本仓库代码为 Apache-2.0。MD5 依赖来自 gmlewis/md5,仅用于 RADIUS 历史协议规定的认证器/PAP/CHAP 互操作,不应被当作新系统密码哈希算法。

    #安全说明

    共享密钥、明文密码和解密后的 User-Password 由调用方管理;库不会持久化它们。RADIUS PAP/MD5 是协议兼容机制,不是现代密码存储方案。生产部署必须使用网络隔离、密钥轮换、重放保护和上层传输安全策略。

    DatagramTransport

    pub(open) trait DatagramTransport {
    fn send(Self, Packet) -> Unit raise TransportError
    fn receive(Self) -> Packet raise TransportError
    }

    An integration boundary for UDP or another datagram implementation.

    RadiusError

    pub(all) suberror RadiusError {
    TooShort
    InvalidCode(Int)
    InvalidLength(Int)
    AttributeTruncated(Int)
    AttributeTooShort(Int)
    InvalidAuthenticator
    Unsupported(String)
    InvalidAttribute(String)
    }

    TransportError

    pub suberror TransportError {
    Closed
    Empty
    Packet(RadiusError)
    }

    Transport-neutral packet exchange primitives.

    AccessDecision

    pub enum AccessDecision {
    Accept
    Reject(String)
    Challenge(String)
    } derive(Eq,
    Debug
    )

    Small policy layer for examples and integration tests.

    MoonRADIUS deliberately does not contain a user database. This module supplies deterministic in-memory decisions so an application can wire its own store into the packet and authenticator APIs.

    AccessDecision::equal

    AccessDecision::not_equal

    fn AccessDecision::not_equal(x : AccessDecision, y : AccessDecision) -> Bool

    AccessPolicy

    pub struct AccessPolicy {
    credentials : Array[Credential]
    require_chap : Bool
    challenge : Bytes
    reply_message : String?
    }

    AccessPolicy::allow

    fn AccessPolicy::allow(self : AccessPolicy, credential : Credential) -> AccessPolicy

    AccessPolicy::new

    AccessPolicy::with_chap

    fn AccessPolicy::with_chap(self : AccessPolicy, challenge : Bytes) -> AccessPolicy

    AccessPolicy::with_reply

    fn AccessPolicy::with_reply(self : AccessPolicy, message : String) -> AccessPolicy

    AccountingEvent

    pub enum AccountingEvent {
    Start
    Interim
    Stop
    AdministrativeOn
    AdministrativeOff
    UnknownStatus(Int)
    MissingStatus
    } derive(Eq,
    Debug
    )

    The lifecycle class of an Accounting-Request.

    AccountingEvent::equal

    AccountingEvent::not_equal

    fn AccountingEvent::not_equal(x : AccountingEvent, y : AccountingEvent) -> Bool

    AccountingRecord

    pub struct AccountingRecord {
    session_id : String?
    status_type : Int?
    input_octets : Int?
    output_octets : Int?
    input_packets : Int?
    output_packets : Int?
    }

    Accounting data extracted from an Accounting-Request.

    AccountingRecord::from_packet

    fn AccountingRecord::from_packet(packet : Packet) -> AccountingRecord

    Attribute

    pub struct Attribute {
    kind : Byte
    value : Bytes
    }

    Attribute::as_integer

    fn Attribute::as_integer(self : Attribute) -> Int?

    Read a four-byte unsigned integer attribute.

    Attribute::framed_protocol

    fn Attribute::framed_protocol(value : FramedProtocolValue) -> Attribute

    Attribute::integer

    fn Attribute::integer(kind : Byte, value : Int) -> Attribute

    Construct an attribute containing a big-endian unsigned integer.

    Attribute::ipv4

    fn Attribute::ipv4(kind : Byte, address : IPv4) -> Attribute

    Attribute::nas_identifier

    fn Attribute::nas_identifier(value : String) -> Attribute

    Attribute::nas_port

    fn Attribute::nas_port(value : Int) -> Attribute

    Attribute::new

    fn Attribute::new(kind : Byte, value : Bytes) -> Attribute

    Attribute::reply

    fn Attribute::reply(value : String) -> Attribute

    Attribute::session_id

    fn Attribute::session_id(value : String) -> Attribute

    Attribute::session_status

    fn Attribute::session_status(value : Int) -> Attribute

    Attribute::termination_cause

    fn Attribute::termination_cause(cause : TerminationCause) -> Attribute

    Attribute::text

    fn Attribute::text(kind : Byte, value : String) -> Attribute

    Construct an attribute from UTF-8 text.

    Attribute::username

    fn Attribute::username(value : String) -> Attribute

    Code

    pub enum Code {
    AccessRequest
    AccessAccept
    AccessReject
    AccountingRequest
    AccountingResponse
    AccessChallenge
    StatusServer
    StatusClient
    Unknown(Int)
    } derive(Eq,
    Debug
    )

    Code::equal

    fn Code::equal(Code, Code) -> Bool

    Code::from_int

    fn Code::from_int(value : Int) -> Code

    Code::not_equal

    fn Code::not_equal(x : Code, y : Code) -> Bool

    Code::to_int

    fn Code::to_int(self : Code) -> Int

    Code::to_repr

    Credential

    pub struct Credential {
    username : String
    password : Bytes
    }

    Credential::new

    fn Credential::new(username : String, password : Bytes) -> Credential

    Diagnostic

    pub struct Diagnostic {
    kind : DiagnosticKind
    offset : Int
    message : String
    }

    Diagnostic::new

    fn Diagnostic::new(kind : DiagnosticKind, offset : Int, message : String) -> Diagnostic

    DiagnosticKind

    pub enum DiagnosticKind {
    Header
    Attribute
    } derive(Eq,
    Debug
    )

    Stable structural diagnostics suitable for CLI and test output.

    DiagnosticKind::equal

    DiagnosticKind::not_equal

    fn DiagnosticKind::not_equal(x : DiagnosticKind, y : DiagnosticKind) -> Bool

    Dictionary

    pub struct Dictionary {
    entries : Array[DictionaryEntry]
    }

    An immutable dictionary assembled from standard and vendor entries.

    Dictionary::add

    fn Dictionary::add(self : Dictionary, entry : DictionaryEntry) -> Dictionary

    Dictionary::lookup_code

    fn Dictionary::lookup_code(self : Dictionary, code : Byte) -> DictionaryEntry?

    Dictionary::lookup_name

    fn Dictionary::lookup_name(self : Dictionary, name : String) -> DictionaryEntry?

    Dictionary::new

    fn Dictionary::new() -> Dictionary

    Dictionary::standard

    fn Dictionary::standard() -> Dictionary

    DictionaryEntry

    pub struct DictionaryEntry {
    name : String
    code : Byte
    kind : String
    }

    A small dictionary entry suitable for vendor extensions.

    DictionaryEntry::new

    fn DictionaryEntry::new(name : String, code : Byte, kind : String) -> DictionaryEntry

    FramedProtocolValue

    pub enum FramedProtocolValue {
    PPP
    SLIP
    ARAP
    GPRS
    None
    } derive(Eq,
    Debug
    )

    FramedProtocolValue::equal

    FramedProtocolValue::not_equal

    FramedProtocolValue::to_int

    fn FramedProtocolValue::to_int(self : FramedProtocolValue) -> Int

    pub struct Header {
    code : Code
    identifier : Byte
    authenticator : Bytes
    }

    Header::new

    fn Header::new(code : Code, identifier : Byte, authenticator : Bytes) -> Header

    IPv4

    pub struct IPv4 {
    first : Byte
    second : Byte
    third : Byte
    fourth : Byte
    }

    Common value constructors for RADIUS attributes.

    IPv4::from_attribute

    fn IPv4::from_attribute(attribute : Attribute) -> IPv4?

    IPv4::new

    fn IPv4::new(first : Int, second : Int, third : Int, fourth : Int) -> IPv4?

    IPv4::to_bytes

    fn IPv4::to_bytes(self : IPv4) -> Bytes

    MemoryTransport

    pub struct MemoryTransport {
    incoming : Array[Bytes]
    outgoing : Array[Bytes]
    closed : Bool
    }

    A deterministic in-memory transport used by examples and tests.

    MemoryTransport::close

    fn MemoryTransport::close(self : MemoryTransport) -> Unit

    MemoryTransport::inject

    fn MemoryTransport::inject(self : MemoryTransport, packet : Bytes) -> Unit

    MemoryTransport::new

    MemoryTransport::pending

    fn MemoryTransport::pending(self : MemoryTransport) -> Int

    Number of packets waiting on either side.

    MemoryTransport::receive

    Receive and decode one injected packet.

    MemoryTransport::send

    fn MemoryTransport::send(self : MemoryTransport, packet : Packet) -> Unit raise TransportError

    Encode and queue a packet for the peer.

    MemoryTransport::take_sent

    fn MemoryTransport::take_sent(self : MemoryTransport) -> Bytes raise TransportError

    Take the next encoded packet sent by this endpoint.

    Packet

    pub struct Packet {
    header : Header
    attributes : Array[Attribute]
    }

    Packet::all_attributes

    fn Packet::all_attributes(self : Packet) -> Array[Attribute]

    Return a defensive copy of all attributes in wire order.

    Packet::attribute

    fn Packet::attribute(self : Packet, kind : Byte) -> Attribute?

    Packet::attributes_of

    fn Packet::attributes_of(self : Packet, kind : Byte) -> Array[Attribute]

    Return all attributes with a given type, preserving wire order.

    Packet::authenticator

    fn Packet::authenticator(self : Packet) -> Bytes

    Return a defensive copy of the request or response authenticator.

    Packet::code

    fn Packet::code(self : Packet) -> Code

    Expose the packet code without requiring callers to inspect the header.

    Packet::copy

    fn Packet::copy(self : Packet) -> Packet

    Return a stable copy of the packet for response construction.

    Packet::diagnostics

    fn Packet::diagnostics(self : Packet) -> Array[Diagnostic]

    Packet::filter_attributes

    fn Packet::filter_attributes(self : Packet, predicate : (Attribute) -> Bool) -> Packet

    Return a packet with the same header and a filtered attribute stream.

    Packet::identifier

    fn Packet::identifier(self : Packet) -> Byte

    Return the RADIUS identifier used to correlate a response.

    Packet::is_access_request

    fn Packet::is_access_request(self : Packet) -> Bool

    Return whether this packet is an Access-Request.

    Packet::is_accounting_request

    fn Packet::is_accounting_request(self : Packet) -> Bool

    Return whether this packet is an Accounting-Request.

    Packet::new

    fn Packet::new(code : Code, identifier : Byte, authenticator : Bytes) -> Packet

    Packet::push_attribute

    fn Packet::push_attribute(self : Packet, attribute : Attribute) -> Packet

    Add an attribute without replacing existing attributes.

    Packet::replace_attribute

    fn Packet::replace_attribute(self : Packet, kind : Byte, replacement : Attribute) -> Packet

    Return a packet with all copies of a type replaced by one value.

    Packet::request

    fn Packet::request(code : Code, identifier : Byte) -> Packet

    Construct a packet with a zero authenticator, useful for requests whose authenticator is filled by a transport or authentication helper.

    Packet::user_password

    fn Packet::user_password(self : Packet, secret : Bytes) -> Bytes? raise RadiusError

    Return the decoded User-Password from a packet, if present.

    Packet::with_attribute

    fn Packet::with_attribute(self : Packet, attribute : Attribute) -> Packet

    Packet::with_attributes

    fn Packet::with_attributes(self : Packet, attributes : Array[Attribute]) -> Packet

    Add several attributes in one operation.

    Packet::with_response_authenticator

    fn Packet::with_response_authenticator(self : Packet, request_authenticator : Bytes, secret : Bytes) -> Packet raise RadiusError

    Attach a calculated response authenticator to a packet.

    Packet::with_vendor_attribute

    fn Packet::with_vendor_attribute(self : Packet, vendor : VendorAttribute) -> Packet raise RadiusError

    Add a Vendor-Specific attribute to a packet.

    Packet::without_attribute

    fn Packet::without_attribute(self : Packet, kind : Byte) -> Packet

    Return a packet with one attribute type removed.

    PacketStats

    pub struct PacketStats {
    attributes : Int
    vendor_attributes : Int
    total_value_bytes : Int
    }

    Collect basic counters without inspecting sensitive attribute values.

    PacketStats::from_packet

    fn PacketStats::from_packet(packet : Packet) -> PacketStats

    RadiusServer

    pub struct RadiusServer {
    config : ServerConfig
    handled : Int
    rejected : Int
    accounting : Int
    }

    RadiusServer::accounting_count

    fn RadiusServer::accounting_count(self : RadiusServer) -> Int

    RadiusServer::handle

    fn RadiusServer::handle(self : RadiusServer, packet : Packet) -> ServerResult raise RadiusError

    Process one already-decoded request.

    RadiusServer::handle_bytes

    fn RadiusServer::handle_bytes(self : RadiusServer, wire : Bytes) -> ServerResult raise RadiusError

    Decode and dispatch a datagram.

    RadiusServer::handled_count

    fn RadiusServer::handled_count(self : RadiusServer) -> Int

    RadiusServer::new

    RadiusServer::rejected_count

    fn RadiusServer::rejected_count(self : RadiusServer) -> Int

    ReplayTable

    pub struct ReplayTable {
    entries : Array[RequestKey]
    capacity : Int
    }

    A bounded replay table suitable for one shared-secret endpoint.

    ReplayTable::classify

    fn ReplayTable::classify(self : ReplayTable, packet : Packet) -> RequestDisposition

    ReplayTable::new

    fn ReplayTable::new(capacity : Int) -> ReplayTable

    ReplayTable::record

    fn ReplayTable::record(self : ReplayTable, packet : Packet) -> RequestDisposition

    Record a new request, evicting the oldest key at capacity.

    ReplayTable::size

    fn ReplayTable::size(self : ReplayTable) -> Int

    RequestDisposition

    pub enum RequestDisposition {
    New
    Retransmission
    Conflict
    } derive(Eq,
    Debug
    )

    Request bookkeeping helpers for datagram servers.

    RADIUS uses an identifier plus request authenticator to correlate retransmissions. This module does not decide policy; it gives an application a bounded, deterministic way to classify repeated requests.

    RequestDisposition::equal

    RequestDisposition::not_equal

    RequestKey

    pub struct RequestKey {
    identifier : Byte
    authenticator : Bytes
    }

    RequestKey::from_packet

    fn RequestKey::from_packet(packet : Packet) -> RequestKey

    RequestKey::same

    fn RequestKey::same(self : RequestKey, other : RequestKey) -> Bool

    ServerConfig

    pub struct ServerConfig {
    secret : Bytes
    access_policy : AccessPolicy
    accounting_enabled : Bool
    reject_malformed : Bool
    }

    ServerConfig::allow_malformed

    fn ServerConfig::allow_malformed(self : ServerConfig) -> ServerConfig

    ServerConfig::new

    fn ServerConfig::new(secret : Bytes, policy : AccessPolicy) -> ServerConfig

    ServerConfig::without_accounting

    fn ServerConfig::without_accounting(self : ServerConfig) -> ServerConfig

    ServerResult

    pub enum ServerResult {
    Response(Packet)
    Drop(Array[String])
    }

    A transport-independent RADIUS request dispatcher.

    This is intentionally a small protocol server core: it owns packet validation, policy dispatch, and response signing, while the application owns sockets, users, persistence, and logging.

    ServerResult::encode

    fn ServerResult::encode(self : ServerResult) -> Bytes? raise RadiusError

    Encode the response from a server result, if one exists.

    TerminationCause

    pub enum TerminationCause {
    UserRequest
    LostCarrier
    LostService
    IdleTimeout
    SessionTimeout
    AdminReset
    AdminReboot
    PortError
    NASRequest
    NASReboot
    } derive(Eq,
    Debug
    )

    TerminationCause::equal

    TerminationCause::not_equal

    fn TerminationCause::not_equal(x : TerminationCause, y : TerminationCause) -> Bool

    TerminationCause::to_int

    fn TerminationCause::to_int(self : TerminationCause) -> Int

    VendorAttribute

    pub struct VendorAttribute {
    vendor_id : Int
    kind : Byte
    value : Bytes
    }

    Typed helpers for standard and vendor-specific attributes.

    VendorAttribute::encode

    fn VendorAttribute::encode(self : VendorAttribute) -> Bytes raise RadiusError

    Encode the value portion of a Vendor-Specific attribute.

    VendorAttribute::new

    fn VendorAttribute::new(vendor_id : Int, kind : Byte, value : Bytes) -> VendorAttribute

    Acct_Input_Octets

    let Acct_Input_Octets : Byte

    Acct_Input_Packets

    let Acct_Input_Packets : Byte

    Acct_Interim_Interval

    let Acct_Interim_Interval : Byte

    Acct_Interim_Update

    let Acct_Interim_Update : Int

    let Acct_Link_Count : Byte

    Acct_Multi_Session_Id

    let Acct_Multi_Session_Id : Byte

    Acct_Off

    let Acct_Off : Int

    Acct_On

    let Acct_On : Int

    Acct_Output_Octets

    let Acct_Output_Octets : Byte

    Acct_Output_Packets

    let Acct_Output_Packets : Byte

    Acct_Session_Id

    let Acct_Session_Id : Byte

    Acct_Session_Time

    let Acct_Session_Time : Byte

    Acct_Start

    let Acct_Start : Int

    Accounting status values defined by RFC 2866.

    Acct_Status_Type

    let Acct_Status_Type : Byte

    Acct_Stop

    let Acct_Stop : Int

    Acct_Terminate_Cause

    let Acct_Terminate_Cause : Byte

    Administrative

    let Administrative : Int

    CHAP_Password

    let CHAP_Password : Byte

    Called_Station_Id

    let Called_Station_Id : Byte

    Calling_Station_Id

    let Calling_Station_Id : Byte

    Class

    let Class : Byte

    Filter_Id

    let Filter_Id : Byte

    Framed

    let Framed : Int

    Framed_IP_Address

    let Framed_IP_Address : Byte

    Framed_IP_Netmask

    let Framed_IP_Netmask : Byte

    Framed_Protocol

    let Framed_Protocol : Byte

    Idle_Timeout

    let Idle_Timeout : Byte

    Login

    let Login : Int

    Common service types.

    NAS_IP_Address

    let NAS_IP_Address : Byte

    NAS_Identifier

    let NAS_Identifier : Byte

    NAS_Port

    let NAS_Port : Byte

    NAS_Prompt

    let NAS_Prompt : Int

    Reply_Message

    let Reply_Message : Byte

    Service_Type

    let Service_Type : Byte

    Session_Timeout

    let Session_Timeout : Byte

    State

    let State : Byte

    Termination_Action

    let Termination_Action : Byte

    User_Name

    let User_Name : Byte

    Standard RADIUS attribute numbers from RFC 2865 and RFC 2866.

    User_Password

    let User_Password : Byte

    Vendor_Specific

    let Vendor_Specific : Byte

    access_response

    fn access_response(request : Packet, decision : AccessDecision, secret : Bytes) -> Packet raise RadiusError

    Build the response corresponding to an Access decision.

    accounting_counter

    fn accounting_counter(packet : Packet, kind : Byte) -> Int

    accounting_event

    fn accounting_event(packet : Packet) -> AccountingEvent

    Classify the status attribute without making callers depend on numeric RFC constants.

    accounting_octets

    fn accounting_octets(packet : Packet) -> Int

    accounting_packets

    fn accounting_packets(packet : Packet) -> Int

    accounting_response

    fn accounting_response(request : Packet, secret : Bytes) -> Packet raise RadiusError

    Create an Accounting-Response with the correct authenticator.

    accounting_session_id

    fn accounting_session_id(packet : Packet) -> String?

    accounting_session_label

    fn accounting_session_label(packet : Packet) -> String

    Return a stable session label for logs and accounting adapters.

    accounting_status

    fn accounting_status(packet : Packet) -> Int?

    chap_response

    fn chap_response(identifier : Byte, password : Bytes, challenge : Bytes) -> Bytes

    Compute the CHAP response value stored in CHAP-Password.

    constant_time_equal

    fn constant_time_equal(left : Bytes, right : Bytes) -> Bool

    Constant-time comparison for authenticators and password-derived values.

    decide_access

    fn decide_access(packet : Packet, policy : AccessPolicy, secret : Bytes) -> AccessDecision raise RadiusError

    Decide an Access-Request using PAP or CHAP, without performing I/O.

    decode

    fn decode(bytes : Bytes) -> Packet raise RadiusError

    decode_vendor_attribute

    fn decode_vendor_attribute(attribute : Attribute) -> VendorAttribute? raise RadiusError

    Decode the value of a Vendor-Specific attribute.

    decrypt_user_password

    fn decrypt_user_password(encrypted : Bytes, secret : Bytes, request_authenticator : Bytes) -> Bytes raise RadiusError

    Decrypt a User-Password attribute according to RFC 2865 section 5.2.

    diagnose

    fn diagnose(bytes : Bytes) -> Array[Diagnostic]

    diagnose_strict

    fn diagnose_strict(bytes : Bytes) -> Array[Diagnostic]

    Diagnose a packet while also checking the declared body boundary.

    diagnose is intentionally non-throwing so command-line tools can show every reliable problem in one pass. This helper adds a precise offset for bytes that exist after the declared packet length.

    diagnostics_are_clean

    fn diagnostics_are_clean(diagnostics : Array[Diagnostic]) -> Bool

    encode

    fn encode(packet : Packet) -> Bytes raise RadiusError

    encode_attributes

    fn encode_attributes(attributes : Array[Attribute]) -> Bytes raise RadiusError

    Encode only the attribute stream, preserving duplicate and unknown entries.

    encrypt_user_password

    fn encrypt_user_password(password : Bytes, secret : Bytes, request_authenticator : Bytes) -> Bytes raise RadiusError

    Encrypt a User-Password attribute according to RFC 2865 section 5.2.

    example_accounting_response

    fn example_accounting_response() -> Packet raise RadiusError

    Build an Accounting-Response for the start example.

    example_accounting_start

    fn example_accounting_start() -> Packet

    NAS Accounting Start record.

    example_catalog

    fn example_catalog() -> Array[String]

    A short textual description suitable for CLI help output.

    example_vpn_challenge

    fn example_vpn_challenge() -> Packet

    VPN Access-Challenge flow with a CHAP challenge.

    example_wifi_accept

    fn example_wifi_accept() -> Packet raise RadiusError

    Build an Access-Accept for the Wi-Fi example.

    example_wifi_request

    fn example_wifi_request() -> Packet

    Enterprise Wi-Fi Access-Request with PAP.

    framed_protocol_values

    fn framed_protocol_values() -> Array[FramedProtocolValue]

    is_accounting_interim

    fn is_accounting_interim(packet : Packet) -> Bool

    is_accounting_start

    fn is_accounting_start(packet : Packet) -> Bool

    is_accounting_stop

    fn is_accounting_stop(packet : Packet) -> Bool

    make_accounting_start

    fn make_accounting_start(identifier : Byte, authenticator : Bytes, session_id : String) -> Packet

    make_accounting_stop

    fn make_accounting_stop(identifier : Byte, authenticator : Bytes, session_id : String, input_octets : Int, output_octets : Int) -> Packet

    make_chap_password

    fn make_chap_password(identifier : Byte, password : Bytes, challenge : Bytes) -> Bytes

    Build the 17-byte CHAP-Password value (identifier followed by digest).

    missing_attributes

    fn missing_attributes(packet : Packet) -> Array[Byte]

    packet_code_name

    fn packet_code_name(code : Code) -> String

    packet_is_request

    fn packet_is_request(packet : Packet) -> Bool

    packet_is_response

    fn packet_is_response(packet : Packet) -> Bool

    packet_is_well_formed

    fn packet_is_well_formed(packet : Packet) -> Bool

    packet_length

    fn packet_length(packet : Packet) -> Int raise RadiusError

    Return the number of bytes occupied by a complete packet.

    packet_summary

    fn packet_summary(packet : Packet) -> String

    Return a compact packet summary for diagnostics and logs.

    request_authenticator_is_present

    fn request_authenticator_is_present(packet : Packet) -> Bool

    request_has_identifier

    fn request_has_identifier(packet : Packet) -> Bool

    required_attributes_for

    fn required_attributes_for(code : Code) -> Array[Byte]

    response_authenticator

    fn response_authenticator(packet : Packet, request_authenticator : Bytes, secret : Bytes) -> Bytes raise RadiusError

    Compute the response authenticator for Access-Accept/Reject/Challenge or Accounting-Response packets.

    response_identifier_matches

    fn response_identifier_matches(request : Packet, response : Packet) -> Bool

    Ensure identifier correlation is preserved across a response.

    response_matches

    fn response_matches(request : Packet, response : Packet) -> Bool

    Decide whether a packet is a legal response to a request code.

    run_examples

    fn run_examples() -> Array[Int] raise RadiusError

    Run all three examples through encode/decode and return wire lengths.

    serve_once

    fn serve_once(server : RadiusServer, transport : MemoryTransport) -> ServerResult raise RadiusError

    Run a complete request/response exchange through the memory transport.

    termination_causes

    fn termination_causes() -> Array[TerminationCause]

    trim_zero_padding

    fn trim_zero_padding(value : Bytes) -> Bytes

    Remove RFC 2865 block padding from a decrypted password.

    validate_access_response

    fn validate_access_response(packet : Packet) -> Bool

    Validate the packet type expected by an Access response workflow.

    validate_accounting_request

    fn validate_accounting_request(packet : Packet) -> Array[String]

    Validate the minimum shape of an Accounting-Request.

    validate_accounting_response

    fn validate_accounting_response(packet : Packet) -> Bool

    Validate the packet type expected by an Accounting response workflow.

    validate_attribute

    fn validate_attribute(attribute : Attribute) -> String?

    Validate attribute lengths and required payload shapes.

    validate_packet

    fn validate_packet(packet : Packet) -> Array[String]

    Return all validation messages without dropping the original attributes.

    validate_wire_rules

    fn validate_wire_rules(packet : Packet) -> Array[String]

    verify_chap

    fn verify_chap(attribute : Attribute, password : Bytes, challenge : Bytes) -> Bool

    Verify a CHAP-Password attribute against a cleartext shared secret.

    verify_response_authenticator

    fn verify_response_authenticator(packet : Packet, request_authenticator : Bytes, secret : Bytes) -> Bool raise RadiusError

    Verify that a received response carries the expected authenticator.