Net-SNMP
view release on metacpan or search on metacpan
lib/Net/SNMP/Security/USM.pm view on Meta::CPAN
# -*- mode: perl -*-
# ============================================================================
package Net::SNMP::Security::USM;
# $Id: USM.pm,v 4.1 2010/09/10 00:01:22 dtown Rel $
# Object that implements the SNMPv3 User-based Security Model.
# Copyright (c) 2001-2010 David M. Town <dtown@cpan.org>
# All rights reserved.
# This program is free software; you may redistribute it and/or modify it
# under the same terms as the Perl 5 programming language system itself.
# ============================================================================
use strict;
use Net::SNMP::Security qw( :ALL );
use Net::SNMP::Message qw(
:msgFlags asn1_itoa OCTET_STRING SEQUENCE INTEGER SNMP_VERSION_3 TRUE FALSE
);
use Crypt::DES();
use Digest::MD5();
use Digest::SHA1();
use Digest::HMAC();
## Version of the Net::SNMP::Security::USM module
our $VERSION = v4.0.1;
## Handle importing/exporting of symbols
use base qw( Net::SNMP::Security );
our @EXPORT_OK;
our %EXPORT_TAGS = (
authprotos => [
qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA )
],
levels => [
qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
SECURITY_LEVEL_AUTHPRIV )
],
models => [
qw( SECURITY_MODEL_ANY SECURITY_MODEL_SNMPV1 SECURITY_MODEL_SNMPV2C
SECURITY_MODEL_USM )
],
privprotos => [
qw( PRIV_PROTOCOL_NONE PRIV_PROTOCOL_DES PRIV_PROTOCOL_AESCFB128
PRIV_PROTOCOL_DRAFT_3DESEDE PRIV_PROTOCOL_DRAFT_AESCFB128
PRIV_PROTOCOL_DRAFT_AESCFB192 PRIV_PROTOCOL_DRAFT_AESCFB256 )
],
);
Exporter::export_ok_tags( qw( authprotos levels models privprotos ) );
$EXPORT_TAGS{ALL} = [ @EXPORT_OK ];
## RCC 3414 - Authentication protocols
sub AUTH_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
## RFC 3414 - Privacy protocols
sub PRIV_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.2.1' } # usmNoPrivProtocol
sub PRIV_PROTOCOL_DES { '1.3.6.1.6.3.10.1.2.2' } # usmDESPrivProtocol
## RFC 3826 - The AES Cipher Algorithm in the SNMP USM
# usmAesCfb128Protocol
sub PRIV_PROTOCOL_AESCFB128 { '1.3.6.1.6.3.10.1.2.4' }
# The privacy protocols below have been implemented using the draft
# specifications intended to extend the User-based Security Model
# defined in RFC 3414. Since the object definitions have not been
# standardized, they have been based on the Extended Security Options
# Consortium MIB found at http://www.snmp.com/eso/esoConsortiumMIB.txt.
# Extension to Support Triple-DES EDE <draft-reeder-snmpv3-usm-3desede-00.txt>
lib/Net/SNMP/Security/USM.pm view on Meta::CPAN
)
)
{
return $_[0]->_error($_[1]->error());
}
return $_[0]->_error($_[1]->error()) if (!$_[1]->length());
# See if the decrypted data starts with a SEQUENCE
# and has a reasonable length.
my $msglen = $_[1]->process(SEQUENCE);
if ((!defined $msglen) || ($msglen > $_[1]->length())) {
return $_[0]->_error('Decryption error');
}
$_[1]->index(0); # Reset the index
DEBUG_INFO('privacy passed');
return TRUE;
}
}
sub _priv_data_init
{
my ($this) = @_;
if (!defined $this->{_priv_key}) {
return $this->_error('The required privKey is not defined');
}
return TRUE if defined $this->{_priv_data};
my $init =
{
PRIV_PROTOCOL_DES, \&_priv_data_init_des,
PRIV_PROTOCOL_DRAFT_3DESEDE, \&_priv_data_init_3desede,
PRIV_PROTOCOL_AESCFB128, \&_priv_data_init_aescfbxxx,
PRIV_PROTOCOL_DRAFT_AESCFB192, \&_priv_data_init_aescfbxxx,
PRIV_PROTOCOL_DRAFT_AESCFB256, \&_priv_data_init_aescfbxxx
};
if (!exists $init->{$this->{_priv_protocol}}) {
return $this->_error(
'The privProtocol "%s" is unknown', $this->{_priv_protocol}
);
}
return $this->${\$init->{$this->{_priv_protocol}}}();
}
sub _priv_data_init_des
{
my ($this) = @_;
if (!defined $this->{_priv_key}) {
return $this->_error('The required privKey is not defined');
}
# Create the DES object
$this->{_priv_data}->{des} =
Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
# Extract the pre-IV
$this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 8, 8;
# Initialize the salt
$this->{_priv_data}->{salt} = int rand ~0;
return TRUE;
}
sub _priv_encrypt_des
{
# my ($this, $priv_params, $plain) = @_;
if (!defined $_[0]->{_priv_data}) {
return $_[0]->_error('The required privacy data is not defined');
}
# Always pad the plain text data. "The actual pad value is
# irrelevant..." according RFC 3414 Section 8.1.1.2. However,
# there are some agents out there that expect "standard block
# padding" where each of the padding byte(s) are set to the size
# of the padding (even for data that is a multiple of block size).
my $pad = 8 - (length($_[2]) % 8);
$_[2] .= pack('C', $pad) x $pad;
# Create and set the salt
if ($_[0]->{_priv_data}->{salt}++ == ~0) {
$_[0]->{_priv_data}->{salt} = 0;
}
$_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
# Create the initial vector (IV)
my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
my $cipher = q{};
# Perform Cipher Block Chaining (CBC)
while ($_[2] =~ /(.{8})/gs) {
$cipher .= $iv = $_[0]->{_priv_data}->{des}->encrypt($1 ^ $iv);
}
return $cipher;
}
sub _priv_decrypt_des
{
# my ($this, $priv_params, $cipher) = @_;
if (!defined $_[0]->{_priv_data}) {
return $_[0]->_error('The required privacy data is not defined');
}
if (length($_[1]) != 8) {
return $_[0]->_error(
'The msgPrivParameters length of %d is invalid', length $_[1]
);
}
if (length($_[2]) % 8) {
return $_[0]->_error(
'The DES cipher length is not a multiple of the block size'
);
}
# Create the initial vector (IV)
my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
my $plain = q{};
# Perform Cipher Block Chaining (CBC)
while ($_[2] =~ /(.{8})/gs) {
$plain .= $iv ^ $_[0]->{_priv_data}->{des}->decrypt($1);
$iv = $1;
}
return $plain;
}
sub _priv_data_init_3desede
{
my ($this) = @_;
if (!defined $this->{_priv_key}) {
return $this->_error('The required privKey is not defined');
}
# Create the 3 DES objects
$this->{_priv_data}->{des1} =
Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
$this->{_priv_data}->{des2} =
Crypt::DES->new(substr $this->{_priv_key}, 8, 8);
$this->{_priv_data}->{des3} =
Crypt::DES->new(substr $this->{_priv_key}, 16, 8);
# Extract the pre-IV
$this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 24, 8;
# Initialize the salt
$this->{_priv_data}->{salt} = int rand ~0;
# Assign a hash algorithm to "bit spread" the salt
if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
$this->{_priv_data}->{hash} = Digest::MD5->new();
} elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
$this->{_priv_data}->{hash} = Digest::SHA1->new();
}
return TRUE;
}
sub _priv_encrypt_3desede
{
# my ($this, $priv_params, $plain) = @_;
if (!defined $_[0]->{_priv_data}) {
return $_[0]->_error('The required privacy data is not defined');
}
# Pad the plain text data using "standard block padding".
my $pad = 8 - (length($_[2]) % 8);
$_[2] .= pack('C', $pad) x $pad;
# Create and set the salt
if ($_[0]->{_priv_data}->{salt}++ == ~0) {
$_[0]->{_priv_data}->{salt} = 0;
}
$_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
# Draft 3DES-EDE for USM Section 5.1.1.1.2 - "To achieve effective
# bit spreading, the complete 8-octet 'salt' value SHOULD be
# hashed using the usmUserAuthProtocol."
if (exists $_[0]->{_priv_data}->{hash}) {
$_[1] = substr $_[0]->{_priv_data}->{hash}->add($_[1])->digest(), 0, 8;
}
# Create the initial vector (IV)
my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
my $cipher = q{};
# Perform Cipher Block Chaining (CBC)
while ($_[2] =~ /(.{8})/gs) {
$cipher .= $iv =
$_[0]->{_priv_data}->{des3}->encrypt(
$_[0]->{_priv_data}->{des2}->decrypt(
$_[0]->{_priv_data}->{des1}->encrypt($1 ^ $iv)
)
);
}
return $cipher;
( run in 2.862 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )