pyrad.packet – packet encoding and decoding

RADIUS packets (RFC 2865): encoding and decoding of packets and attributes, password encryption, request and response authenticators and the Message-Authenticator.

class pyrad.packet.Packet(code=0, id=None, secret=b'', authenticator=None, **attributes)[source]

A RADIUS packet, with a dictionary interface to its attributes.

Since RADIUS allows repeated attributes, values are always lists, and the order of the attributes is kept when encoding and decoding. Assigning pkt['Name'] = value replaces all values of the attribute, AddAttribute() appends to them.

With attribute names as keys pyrad encodes and decodes the values according to the dictionary; string values that are not valid UTF-8 are returned as bytes. With the attribute code (or a (vendor, code) tuple for vendor attributes) as key you work with the raw bytes values; attributes which are not in the dictionary are always keyed like this.

Attributes with the has_tag flag (RFC 2868) take a tag after the name, for example pkt['Tunnel-Type:1']. Reading without a tag returns the values of all tags, assigning (also with a tag) replaces the values of all tags; del pkt['Name:tag'] is not supported. An untagged Tunnel-Password is sent with tag 0.

The value of a TLV attribute is a dict which maps the sub-attribute names to their values. Sub-attributes can also be read, set, deleted and tested with in by their own name.

Normally you will not use this class directly, but one of the AuthPacket, AcctPacket or CoAPacket classes.

Variables:
  • code – packet type code, e.g. AccessRequest

  • id – packet identifier (0-255)

  • authenticator – Request or Response Authenticator, None until it is created

  • secret – RADIUS secret

  • dict – RADIUS dictionary

  • raw_packet – the data the packet was decoded from, or None

  • message_authenticator – whether the packet has (or gets when it is encoded) a Message-Authenticator

  • request_authenticator – in a reply, the Request Authenticator of the request, needed to decrypt salt encrypted attributes

Parameters:
  • code (int) – packet type code

  • id (int) – packet identifier (0-255), defaults to the next value of the module-wide counter, see pyrad.packet.CreateID()

  • secret (bytes) – RADIUS secret

  • authenticator (bytes) – Request or Response Authenticator

  • dict (pyrad.dictionary.Dictionary) – RADIUS dictionary; without one only integer keys (attribute codes) work

  • packet (bytes) – received data to decode

  • message_authenticator (bool) – add a Message-Authenticator when the packet is encoded

  • attributes – attributes to add, with - in the attribute names replaced by _

Raises:
  • PacketError – packet cannot be decoded

  • TypeError – secret or authenticator is not bytes

AddAttribute(key, value)[source]

Add an attribute to the packet. The values are appended to existing values of the attribute. Raw values of attributes which are not in the dictionary are set with pkt[code] = [value] instead.

Parameters:
  • key (str) – attribute name, with an optional tag for attributes with the has_tag flag ('Tunnel-Type:1')

  • value – value or list of values, of the Python type of the attribute’s data type (e.g. str, int or bytes)

static CreateAuthenticator()[source]

Create a packet authenticator. All RADIUS packets contain a sixteen byte authenticator which is used to authenticate replies from the RADIUS server and in the password hiding algorithm. This function returns a suitable random string that can be used as an authenticator.

Returns:

valid packet authenticator

Return type:

bytes

CreateID()[source]

Return a random packet Identifier (0-255).

All RADIUS requests have an Identifier which is used to match replies to requests and to detect duplicate requests. Packets created without an id get the next value of the module-level counter pyrad.packet.CreateID() instead.

Returns:

packet Identifier

Return type:

int

CreateReply(**attributes)[source]

Create a reply to this packet.

The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes (RFC 2865 section 5.33). Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. The reply code defaults to 0, pass code. With packet=<bytes> a received reply is decoded.

Parameters:

attributes – arguments of Packet and attributes to add

Returns:

the reply

Return type:

pyrad.packet.Packet

DecodePacket(packet)[source]

Decode a packet as received from the network.

The code, id, authenticator and attributes of this packet are replaced by the decoded ones.

Parameters:

packet (bytes) – raw packet

Raises:

PacketError – the packet cannot be decoded

ReplyPacket()[source]

Encode the packet as a reply, ready to be sent to a client.

The Response Authenticator is calculated from the Request Authenticator in authenticator, and a Message-Authenticator is added if message_authenticator is set.

Returns:

raw packet

Return type:

bytes

SaltCrypt(value)[source]

Encrypt a value with the salt encryption of RFC 2868 section 3.5 (attributes with encrypt=2).

Values longer than 239 octets don’t fit into an attribute; the encoding of the packet fails for them.

Parameters:

value (str or bytes) – plaintext value

Returns:

encrypted value including salt

Return type:

bytes

Raises:

ValueError – if the value is longer than 255 octets

SaltDecrypt(value)[source]

Decrypt a value encrypted with the salt encryption of RFC 2868 section 3.5 (attributes with encrypt=2).

For a reply, request_authenticator must be set to the Request Authenticator of the request (the clients do that).

Parameters:

value (bytes) – encrypted value including salt

Returns:

decrypted value

Return type:

bytes

Raises:

PacketError – the value has an invalid length

VerifyReply(reply, rawreply=None, enforce_ma=False)[source]

Verify that a reply belongs to this request.

Checks the packet ID and the Response Authenticator and, as countermeasure against the BlastRADIUS attack (CVE-2024-3596), the Message-Authenticator (always verified if the reply has one) and, for Access-Request and Status-Server, that the reply has no Proxy-State which was not sent.

Parameters:
  • reply (pyrad.packet.Packet) – reply packet

  • rawreply (bytes) – reply as received from the network, defaults to the raw packet the reply was decoded from

  • enforce_ma (bool) – require a Message-Authenticator in replies to Access-Request and Status-Server packets

Returns:

True if the reply is valid else False

Return type:

bool

add_message_authenticator()[source]

Add a Message-Authenticator (RFC 2869 section 5.14) as first attribute, as recommended as countermeasure against the BlastRADIUS attack (CVE-2024-3596). The value is calculated when the packet is encoded.

get(key, failobj=None)[source]

Return the values of an attribute, or failobj if the packet does not have it.

Parameters:
  • key – attribute name, code or (vendor, code) tuple

  • failobj – value to return if the attribute is missing

Returns:

list of values, or failobj

get_message_authenticator()[source]

Calculate the Message-Authenticator and return the message_authenticator flag.

has_key(key)

Return whether the packet has the attribute key.

has_key(key) is a deprecated alias of key in pkt.

keys()[source]

Return the attribute keys of the packet, in order.

Attributes in the dictionary are listed by name (a TLV by the TLV name, not by its sub-attributes), undefined attributes by their code or (vendor, code) tuple.

Returns:

attribute names and codes

Return type:

list

verify_message_authenticator(secret=None, original_authenticator=None, original_code=None)[source]

Verify the Message-Authenticator of the packet.

To verify a received reply, the authenticator and code of the request must be passed as original_authenticator and original_code (VerifyReply() does that).

Parameters:
  • secret (bytes) – RADIUS secret, defaults to secret

  • original_authenticator (bytes) – Request Authenticator of the request, for a reply

  • original_code (int) – code of the request, for a reply

Returns:

True if the Message-Authenticator is valid else False

Return type:

bool

Raises:

Exception – the packet has no Message-Authenticator; check message_authenticator first

class pyrad.packet.AuthPacket(code=1, id=None, secret=b'', authenticator=None, auth_type='pap', **attributes)[source]

RADIUS authentication packet: Access-Request, Status-Server sent to the authentication port, and their replies.

Parameters:
  • code (int) – packet type code

  • id (int) – packet identifier, see Packet

  • secret (bytes) – RADIUS secret

  • authenticator (bytes) – Request Authenticator, created when needed

  • auth_type (str) – 'pap', or 'eap-md5' to authenticate with EAP-MD5 when the packet is sent with pyrad.client.Client.SendPacket() or pyrad.client_async.ClientAsync.SendPacket()

  • attributes – dict, packet, message_authenticator and attributes to add, see Packet

CreateReply(**attributes)[source]

Create a reply to this packet, an Access-Accept by default.

The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes (RFC 2865 section 5.33). Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. Passing code raises TypeError, set reply.code instead (e.g. to AccessReject).

A reply to an Access-Request or Status-Server gets a Message-Authenticator unless message_authenticator=False is passed. With packet=<bytes> a received reply is decoded.

Parameters:

attributes – arguments of AuthPacket and attributes to add

Returns:

the reply

Return type:

pyrad.packet.AuthPacket

PwCrypt(password)[source]

Encrypt a password for the User-Password attribute.

RADIUS hides passwords with an algorithm based on the MD5 hash of the Request Authenticator and the secret (RFC 2865 section 5.2): pkt['User-Password'] = pkt.PwCrypt(password). If no authenticator is set, one is created. Changing the authenticator afterwards makes the encrypted password invalid.

Parameters:

password (str or bytes) – plaintext password

Returns:

obfuscated version of the password

Return type:

bytes

Raises:

ValueError – if the password is longer than 128 octets

PwDecrypt(password)[source]

Decrypt a User-Password (RFC 2865 section 5.2).

RADIUS hides passwords with an algorithm based on the MD5 hash of the Request Authenticator and the secret; this reverses it.

The password is decoded as UTF-8, the encoding RFC 2865 uses for text; invalid UTF-8 sequences (for example if the secret is wrong) are ignored.

Parameters:

password (bytes or str) – encrypted password

Returns:

plaintext password

Return type:

str

Raises:

PacketError – the length is not a multiple of 16 octets

RequestPacket()[source]

Encode the packet as a request, ready to be sent to a server.

A Request Authenticator and id are created if not set yet.

Returns:

raw packet

Return type:

bytes

VerifyAuthRequest()[source]

Check whether the authenticator is an accounting-style request authenticator: the MD5 hash of the packet header, 16 zero octets, the attributes and the secret (as for Accounting-Request, RFC 2866 section 3).

This is not a verification of Access-Request packets: their Request Authenticator is a random number (RFC 2865 section 3), so this check fails for them. Use verify_message_authenticator to authenticate an Access-Request or Status-Server packet with a Message-Authenticator.

Only for packets decoded from received data (packet=), it fails for packets built locally.

Returns:

True if verification passed else False

Return type:

bool

VerifyChapPasswd(userpwd)[source]

Verify the CHAP-Password of an Access-Request (RFC 2865 section 2.2). The CHAP-Challenge attribute is used as challenge if present, else the request authenticator.

Parameters:

userpwd (str or bytes) – plaintext password; surrounding whitespace is stripped from a str, not from bytes

Returns:

True if the password matches else False, also if there is no CHAP-Password attribute

Return type:

bool

class pyrad.packet.AcctPacket(code=4, id=None, secret=b'', authenticator=None, **attributes)[source]

RADIUS accounting packet: Accounting-Request, Status-Server sent to the accounting port, and their replies.

Parameters:
  • code (int) – packet type code

  • id (int) – packet identifier, see Packet

  • secret (bytes) – RADIUS secret

  • authenticator (bytes) – Request Authenticator; for an Accounting-Request it is calculated when the packet is encoded

  • attributes – dict, packet, message_authenticator and attributes to add, see Packet

CreateReply(**attributes)[source]

Create a reply to this packet, an Accounting-Response.

The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes. Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. Passing code raises TypeError, set reply.code instead.

A reply to a Status-Server gets a Message-Authenticator unless message_authenticator=False is passed. With packet=<bytes> a received reply is decoded.

Parameters:

attributes – arguments of AcctPacket and attributes to add

Returns:

the reply

Return type:

pyrad.packet.AcctPacket

RequestPacket()[source]

Encode the packet as a request, ready to be sent to a server.

The Request Authenticator of an Accounting-Request is calculated over the packet (RFC 2866 section 3) and stored in authenticator.

A Status-Server packet (RFC 5997 section 3) gets a random Request Authenticator, like an Access-Request, and a Message-Authenticator unless message_authenticator is set to False.

Returns:

raw packet

Return type:

bytes

VerifyAcctRequest()[source]

Verify the Request Authenticator of a received request.

The Request Authenticator of a Status-Server packet is random (RFC 5997 section 3), for these packets the Message-Authenticator is verified instead, and a packet without one fails verification.

Only for packets decoded from received data (packet=), it fails for packets built locally.

Returns:

True if verification passed else False

Return type:

bool

class pyrad.packet.CoAPacket(code=43, id=None, secret=b'', authenticator=None, **attributes)[source]

RADIUS Dynamic Authorization packet (RFC 5176): CoA-Request, Disconnect-Request and their replies.

Parameters:
  • code (int) – packet type code, CoARequest or DisconnectRequest for a request

  • id (int) – packet identifier, see Packet

  • secret (bytes) – RADIUS secret

  • authenticator (bytes) – Request Authenticator; for a request it is calculated when the packet is encoded

  • attributes – dict, packet, message_authenticator and attributes to add, see Packet

CreateReply(**attributes)[source]

Create a reply to this packet: a Disconnect-ACK for a Disconnect-Request, otherwise a CoA-ACK.

Set reply.code for a NAK (CoANAK or DisconnectNAK). Passing code raises TypeError.

The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes. Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. With packet=<bytes> a received reply is decoded.

Parameters:

attributes – arguments of CoAPacket and attributes to add

Returns:

the reply

Return type:

pyrad.packet.CoAPacket

RequestPacket()[source]

Encode the packet as a request, ready to be sent to a server.

The Request Authenticator is calculated over the packet (RFC 5176 section 3.3) and stored in authenticator.

Returns:

raw packet

Return type:

bytes

VerifyCoARequest()[source]

Verify the Request Authenticator of a received CoA-Request or Disconnect-Request (RFC 5176 section 3.3).

Only for packets decoded from received data (packet=), it fails for packets built locally.

Returns:

True if verification passed else False

Return type:

bool

class pyrad.packet.PacketError[source]

Raised for a packet or attribute value that cannot be decoded, e.g. an invalid length.

Constants

The pyrad.packet module defines several common constants that are useful when dealing with RADIUS packets.

The following packet codes are defined:

Constant name

Value

AccessRequest

1

AccessAccept

2

AccessReject

3

AccountingRequest

4

AccountingResponse

5

AccessChallenge

11

StatusServer

12

StatusClient

13

DisconnectRequest

40

DisconnectACK

41

DisconnectNAK

42

CoARequest

43

CoAACK

44

CoANAK

45