SnmpKit.SnmpLib.PDU.V3Encoder (snmpkit v2.0.1)

SNMPv3 message encoding and decoding with User Security Model (USM) support.

This module implements the SNMPv3 message format as specified in RFC 3412 and RFC 3414, providing authentication and privacy protection for SNMP communications.

SNMPv3 Message Structure

SNMPv3 messages have a complex hierarchical structure:

SNMPv3Message ::= SEQUENCE {
    msgVersion INTEGER (0..2147483647),
    msgGlobalData HeaderData,
    msgSecurityParameters OCTET STRING,
    msgData ScopedPduData
}

HeaderData ::= SEQUENCE {
    msgID INTEGER (0..2147483647),
    msgMaxSize INTEGER (484..2147483647),
    msgFlags OCTET STRING (SIZE(1)),
    msgSecurityModel INTEGER (1..2147483647)
}

ScopedPduData ::= CHOICE {
    plaintext ScopedPDU,
    encryptedPDU OCTET STRING
}

ScopedPDU ::= SEQUENCE {
    contextEngineID OCTET STRING,
    contextName OCTET STRING,
    data ANY
}

Security Processing

The module integrates with the security subsystem to provide:

  • Message authentication using HMAC algorithms
  • Message encryption using AES/DES algorithms
  • Time synchronization and engine discovery
  • Replay attack protection

Usage Examples

Encoding a SNMPv3 Message

# Create security user
user = %{
  security_name: "testuser",
  auth_protocol: :sha256,
  priv_protocol: :aes128,
  auth_key: derived_auth_key,
  priv_key: derived_priv_key,
  engine_id: "local_engine"
}

# Create SNMPv3 message
message = %{
  version: 3,
  msg_id: 12345,
  msg_max_size: 65507,
  msg_flags: %{auth: true, priv: true, reportable: true},
  msg_security_model: 3,
  msg_security_parameters: "",  # Will be generated
  msg_data: %{
    context_engine_id: "target_engine",
    context_name: "",
    pdu: pdu
  }
}

# Encode with security
{:ok, encoded} = SnmpKit.SnmpLib.PDU.V3Encoder.encode_message(message, user)

Decoding a SNMPv3 Message

{:ok, decoded} = SnmpKit.SnmpLib.PDU.V3Encoder.decode_message(binary_data, user)

Security Notes

  • Authentication is required for privacy (encryption)
  • Engine discovery must be performed before authenticated communication
  • Time synchronization is required to prevent replay attacks
  • Message IDs should be unique to prevent duplicate processing

Wire-format details

  • msgAuthenticationParameters is the HMAC computed over the entire serialized message with the parameter field itself zeroed (RFC 3414 6.3.1 / 7.3.1). On decode the MAC is recomputed over the bytes actually received, never over a re-encoding, so any peer's BER choices are accepted.
  • Encrypted messages carry the ciphertext in an OCTET STRING msgData; the MAC covers that OCTET STRING, exactly as transmitted.
  • AES/DES IVs are derived from msgAuthoritativeEngineBoots/Time and the 8-octet salt in msgPrivacyParameters (RFC 3414 8.1.1.1, RFC 3826 3.1.2.1).
  • Decoded messages expose the received USM parameters under :security_parameters (see SnmpKit.SnmpLib.Security.security_params/0) and the raw octets under :msg_security_parameters.

Summary

Functions

Creates a discovery message for engine ID discovery.

Decodes a SNMPv3 message with security processing.

Decodes only the header and security parameters of a SNMPv3 message.

Decodes a raw msgSecurityParameters OCTET STRING as USM parameters.

Encodes a SNMPv3 message with security processing.

Types

scoped_pdu()

security_params()

@type security_params() :: SnmpKit.SnmpLib.Security.security_params()

security_user()

@type security_user() :: SnmpKit.SnmpLib.Security.security_user()

v3_message()

Functions

create_discovery_message(msg_id \\ :rand.uniform(2_147_483_647))

@spec create_discovery_message(non_neg_integer()) :: v3_message()

Creates a discovery message for engine ID discovery.

decode_message(data, user \\ nil)

@spec decode_message(binary(), security_user() | nil) ::
  {:ok, v3_message()} | {:error, atom()}

Decodes a SNMPv3 message with security processing.

Parameters

  • data - Binary SNMPv3 message data
  • user - Security user configuration (optional for discovery messages)

Returns

  • {:ok, message} on success. Besides the header fields and :msg_data (the decoded ScopedPDU) the map carries :msg_security_parameters (raw octets) and :security_parameters (decoded USM parameters, or nil when they are not USM-shaped).
  • {:error, reason} on failure. An encrypted message decoded without a user yields {:error, :user_required_for_privacy}; use decode_message_header/1 to read the security parameters first and look the user up.

decode_message_header(data)

@spec decode_message_header(binary()) :: {:ok, map()} | {:error, atom()}

Decodes only the header and security parameters of a SNMPv3 message.

No authentication or decryption is performed. This is what an authoritative engine uses to find msgUserName before it can select the user whose keys verify the message.

Returns the same map as decode_message/2 minus :msg_data.

decode_security_parameters(security_params)

@spec decode_security_parameters(binary()) ::
  {:ok, security_params()} | {:error, atom()}

Decodes a raw msgSecurityParameters OCTET STRING as USM parameters.

Returns a map in the SnmpKit.SnmpLib.Security.security_params/0 shape.

encode_message(message, user \\ nil)

@spec encode_message(v3_message(), security_user() | nil) ::
  {:ok, binary()} | {:error, atom()}

Encodes a SNMPv3 message with security processing.

Parameters

  • message - SNMPv3 message structure
  • user - Security user configuration (optional for discovery messages)

Returns

  • {:ok, binary()} on success
  • {:error, reason} on failure

Examples

{:ok, encoded} = encode_message(snmpv3_message, security_user)
{:ok, discovery_msg} = encode_message(discovery_message, nil)