SnmpKit.SnmpLib.Security.Keys (snmpkit v2.0.1)
Key derivation and management for SNMPv3 User Security Model.
Implements RFC 3414 compliant key derivation functions for converting user passwords into cryptographic keys suitable for authentication and privacy operations.
Key Derivation Process
SNMPv3 uses a two-step key derivation process:
- Password Localization: Transform user password into a localized key using the authoritative engine ID
- Key Expansion: Derive authentication and privacy keys from the localized key based on protocol requirements
Security Properties
- Keys are derived deterministically from passwords and engine IDs
- Different engine IDs produce different keys for the same password
- Key derivation uses cryptographic hash functions for security
- Derived keys cannot be used to recover original passwords
- Each protocol type (auth/priv) uses different key derivation parameters
Supported Algorithms
Authentication Key Derivation
- MD5: RFC 3414 compliant (deprecated)
- SHA-1: RFC 3414 compliant (deprecated)
- SHA-224: RFC 7860 compliant
- SHA-256: RFC 7860 compliant (recommended)
- SHA-384: RFC 7860 compliant
- SHA-512: RFC 7860 compliant
Privacy Key Derivation
Per RFC 3414 section 8.1.1.1 and RFC 3826 section 1.2 the privacy key is the authentication protocol's password-to-key algorithm applied to the privacy password and localized with the engine ID, truncated to the cipher's key size:
- DES: 16 bytes (8-byte DES key followed by the 8-byte pre-IV)
- AES-128: 16 bytes
- AES-192: 24 bytes
- AES-256: 32 bytes
When the hash output is shorter than the cipher key (e.g. MD5/SHA-1 with
AES-192/256) the key is extended. Two extension schemes exist in the wild:
:reeder (draft-reeder-snmpv3-usm-3desede, the net-snmp default) and
:blumenthal (draft-blumenthal-aes-usm). Pass key_extension: to choose;
the default is :reeder.
Usage Examples
Authentication Key Derivation
# Derive SHA-256 authentication key
engine_id = <<0x80, 0x00, 0x1f, 0x88, 0x80, 0x01, 0x02, 0x03, 0x04>>
password = "authentication_password"
{:ok, auth_key} = SnmpKit.SnmpLib.Security.Keys.derive_auth_key(:sha256, password, engine_id)Privacy Key Derivation
# Derive AES-256 privacy key for a user authenticating with SHA-256
{:ok, priv_key} = SnmpKit.SnmpLib.Security.Keys.derive_priv_key(
:aes256, password, engine_id, auth_protocol: :sha256
)
# Or reuse the localized authentication key when both passwords are the same
{:ok, priv_key} = SnmpKit.SnmpLib.Security.Keys.derive_priv_key_from_auth(:aes256, auth_key, engine_id)Key Validation
# Validate key strength
:ok = SnmpKit.SnmpLib.Security.Keys.validate_password_strength(password)
{:error, :too_short} = SnmpKit.SnmpLib.Security.Keys.validate_password_strength("weak")
Summary
Functions
Derives authentication key from password and engine ID.
Derives multiple authentication keys for different protocols from the same password.
Derives privacy key from password and engine ID.
Derives privacy key from an existing localized authentication key.
Generates a cryptographically secure random password.
Securely compares two derived keys to prevent timing attacks.
Validates password strength according to SNMPv3 security guidelines.
Types
Functions
@spec derive_auth_key(auth_protocol(), password(), engine_id()) :: {:ok, derived_key()} | {:error, atom()}
Derives authentication key from password and engine ID.
Implements RFC 3414 key localization algorithm for authentication protocols. The derived key is specific to the combination of password, protocol, and engine ID.
Parameters
protocol: Authentication protocol (:md5, :sha1, :sha256, etc.)password: User password (minimum 8 characters recommended)engine_id: Authoritative engine ID (5-32 bytes)
Returns
{:ok, key}: Successfully derived authentication key{:error, reason}: Key derivation failed
Examples
# SHA-256 authentication key (recommended)
{:ok, key} = SnmpKit.SnmpLib.Security.Keys.derive_auth_key(
:sha256, "my_secure_password", engine_id
)
# Legacy MD5 key derivation
{:ok, key} = SnmpKit.SnmpLib.Security.Keys.derive_auth_key(
:md5, "legacy_password", engine_id
)
@spec derive_auth_keys_multi([auth_protocol()], password(), engine_id()) :: {:ok, %{required(auth_protocol()) => derived_key()}} | {:error, atom()}
Derives multiple authentication keys for different protocols from the same password.
Useful when supporting multiple authentication protocols simultaneously.
Examples
protocols = [:sha256, :sha384, :sha512]
{:ok, keys} = SnmpKit.SnmpLib.Security.Keys.derive_auth_keys_multi(protocols, password, engine_id)
# Returns: %{sha256: key1, sha384: key2, sha512: key3}
@spec derive_priv_key(priv_protocol(), password(), engine_id(), keyword()) :: {:ok, derived_key()} | {:error, atom()}
Derives privacy key from password and engine ID.
Implements RFC 3414 section 8.1.1.1 / RFC 3826 section 1.2: the privacy password is run through the authentication protocol's password-to-key algorithm, localized with the engine ID, and cut (or extended) to the cipher key size.
Parameters
protocol: Privacy protocol (:des, :aes128, :aes192, :aes256)password: User password for privacyengine_id: Authoritative engine IDopts::auth_protocol- the user's authentication protocol, which selects the hash (default:md5, the RFC 3414 baseline):key_extension-:reeder(default, net-snmp compatible) or:blumenthalfor AES keys longer than the hash output
Returns
{:ok, key}: Successfully derived privacy key{:error, reason}: Key derivation failed
Examples
# AES-256 privacy key for a SHA-256 user (recommended)
{:ok, key} = SnmpKit.SnmpLib.Security.Keys.derive_priv_key(
:aes256, "privacy_password", engine_id, auth_protocol: :sha256
)
# DES privacy key (legacy, MD5 based)
{:ok, key} = SnmpKit.SnmpLib.Security.Keys.derive_priv_key(
:des, "legacy_privacy_password", engine_id
)
@spec derive_priv_key_from_auth( priv_protocol(), derived_key(), engine_id(), keyword() ) :: {:ok, derived_key()} | {:error, atom()}
Derives privacy key from an existing localized authentication key.
Only valid when the privacy password equals the authentication password: the
localized key is then the same for both, and this skips the expensive
password-to-key step. The hash used for key extension is inferred from the
key length unless auth_protocol: is given.
Examples
{:ok, auth_key} = derive_auth_key(:sha256, password, engine_id)
{:ok, priv_key} = SnmpKit.SnmpLib.Security.Keys.derive_priv_key_from_auth(
:aes256, auth_key, engine_id
)
@spec generate_secure_password(pos_integer()) :: password()
Generates a cryptographically secure random password.
Examples
password = SnmpKit.SnmpLib.Security.Keys.generate_secure_password(16)
# Returns: "K7mN9pQ2rT8vW3xZ" (example)
@spec secure_compare(derived_key(), derived_key()) :: boolean()
Securely compares two derived keys to prevent timing attacks.
Examples
true = SnmpKit.SnmpLib.Security.Keys.secure_compare(key1, key1)
false = SnmpKit.SnmpLib.Security.Keys.secure_compare(key1, key2)
Validates password strength according to SNMPv3 security guidelines.
Requirements
- Minimum 8 characters (RFC recommendation)
- Should contain mix of character types for security
- Should not be based on dictionary words
Examples
:ok = SnmpKit.SnmpLib.Security.Keys.validate_password_strength("strong_password_123")
{:error, :too_short} = SnmpKit.SnmpLib.Security.Keys.validate_password_strength("weak")
{:warning, :weak_complexity} = SnmpKit.SnmpLib.Security.Keys.validate_password_strength("password")