summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/mibs/PYSNMP-MIB.txt51
-rw-r--r--docs/mibs/PYSNMP-USM-MIB.txt192
-rw-r--r--pysnmp/proto/secmod/rfc3414/__init__.py3
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/__init__.py0
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/base.py8
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py113
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/hmacsha.py112
-rw-r--r--pysnmp/proto/secmod/rfc3414/localkey.py38
-rw-r--r--pysnmp/proto/secmod/rfc3414/priv/__init__.py0
-rw-r--r--pysnmp/proto/secmod/rfc3414/priv/base.py4
-rw-r--r--pysnmp/proto/secmod/rfc3414/priv/des.py117
-rw-r--r--pysnmp/proto/secmod/rfc3414/service.py689
-rw-r--r--pysnmp/smi/exval.py6
13 files changed, 1333 insertions, 0 deletions
diff --git a/docs/mibs/PYSNMP-MIB.txt b/docs/mibs/PYSNMP-MIB.txt
new file mode 100644
index 0000000..fce3ac5
--- /dev/null
+++ b/docs/mibs/PYSNMP-MIB.txt
@@ -0,0 +1,51 @@
+PYSNMP-MIB DEFINITIONS ::= BEGIN
+
+--
+-- Top-level infrastructure of the PySNMP project enterprise MIB tree
+--
+
+IMPORTS
+ MODULE-IDENTITY, enterprises FROM SNMPv2-SMI;
+
+pysnmp MODULE-IDENTITY
+ LAST-UPDATED "200505140000Z"
+ ORGANIZATION "pysnmp.sf.net"
+ CONTACT-INFO
+ "email: ilya@glas.net"
+ DESCRIPTION
+ "Top-level infrastructure of the PySNMP project enterprise MIB tree"
+ REVISION "200505140000Z"
+ DESCRIPTION
+ "Initial revision"
+ ::= { enterprises 20408 }
+
+
+--
+-- PySNMP enterprise-specific management objects
+--
+
+pysnmpObjects OBJECT IDENTIFIER ::= {pysnmp 1}
+pysnmpExamples OBJECT IDENTIFIER ::= {pysnmp 2}
+pysnmpEnumerations OBJECT IDENTIFIER ::= {pysnmp 3}
+pysnmpModuleIDs OBJECT IDENTIFIER ::= {pysnmpEnumerations 1}
+pysnmpAgentOIDs OBJECT IDENTIFIER ::= {pysnmpEnumerations 2}
+pysnmpDomains OBJECT IDENTIFIER ::= {pysnmpEnumerations 3}
+pysnmpExperimental OBJECT IDENTIFIER ::= {pysnmp 9999}
+
+--
+-- Notifications
+--
+
+pysnmpNotificationPrefix OBJECT IDENTIFIER ::= {pysnmp 4}
+pysnmpNotifications OBJECT IDENTIFIER ::= {pysnmpNotificationPrefix 0}
+pysnmpNotificationObjects OBJECT IDENTIFIER ::= {pysnmpNotificationPrefix 1}
+
+--
+-- Conformance
+--
+
+pysnmpConformance OBJECT IDENTIFIER ::= {pysnmp 5}
+pysnmpCompliances OBJECT IDENTIFIER ::= {pysnmpConformance 1}
+pysnmpGroups OBJECT IDENTIFIER ::= {pysnmpConformance 2}
+
+END
diff --git a/docs/mibs/PYSNMP-USM-MIB.txt b/docs/mibs/PYSNMP-USM-MIB.txt
new file mode 100644
index 0000000..7ceddaf
--- /dev/null
+++ b/docs/mibs/PYSNMP-USM-MIB.txt
@@ -0,0 +1,192 @@
+PYSNMP-USM-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY,
+ OBJECT-TYPE
+ FROM SNMPv2-SMI
+ SnmpAdminString
+ FROM SNMP-FRAMEWORK-MIB
+ usmUserEntry
+ FROM SNMP-USER-BASED-SM-MIB
+ RowStatus
+ FROM SNMPv2-TC
+ pysnmpModuleIDs
+ FROM PYSNMP-MIB;
+
+pysnmpUsmMIB MODULE-IDENTITY
+ LAST-UPDATED "200505140000Z" -- 14 May 2005, midnight
+ ORGANIZATION "The PySNMP project"
+ CONTACT-INFO "E-mail: ilya@glas.net
+ Subscribe: pysnmp-users-request@lists.sourceforge.net"
+ DESCRIPTION
+ "This MIB module defines objects specific to User
+ Security Model (USM) implementation at PySNMP."
+ REVISION "200505140000Z" -- 14 May 2005, midnight
+ DESCRIPTION "The Initial Revision"
+ ::= { pysnmpModuleIDs 1 }
+
+-- Administrative assignments ****************************************
+
+pysnmpUsmMIBObjects OBJECT IDENTIFIER ::= { pysnmpUsmMIB 1 }
+pysnmpUsmMIBConformance OBJECT IDENTIFIER ::= { pysnmpUsmMIB 2 }
+
+-- Implementation-specific SNMP engine configuration
+
+pysnmpUsmCfg OBJECT IDENTIFIER ::= { pysnmpUsmMIBObjects 1 }
+
+pysnmpUsmDiscoverable OBJECT-TYPE
+ SYNTAX INTEGER { notDiscoverable(0), discoverable(1) }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION "Whether SNMP engine would support its discovery by
+ responding to unknown clients."
+ DEFVAL { discoverable }
+ ::= { pysnmpUsmCfg 1 }
+
+pysnmpUsmDiscovery OBJECT-TYPE
+ SYNTAX INTEGER { doNotDiscover(0), doDiscover(1) }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION "Whether SNMP engine would try to figure out the EngineIDs
+ of its peers by sending discover requests."
+ DEFVAL { doDiscover }
+ ::= { pysnmpUsmCfg 2 }
+
+-- The usmUser Group ************************************************
+
+pysnmpUsmUser OBJECT IDENTIFIER ::= { pysnmpUsmMIBObjects 3 }
+
+--
+-- The pysnmpUsmSecretTable contains a database of USM users passphrases
+-- used for key localization. This table may be consulted during SNMP engine-ID
+-- autodiscovery procedure.
+--
+
+pysnmpUsmSecretTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PysnmpUsmSecretEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The table of USM users passphrases configured in the SNMP
+ engine's Local Configuration Datastore (LCD)."
+ ::= { pysnmpUsmMIBObjects 2 }
+
+pysnmpUsmSecretEntry OBJECT-TYPE
+ SYNTAX PysnmpUsmSecretEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a particular USM user credentials."
+ INDEX { IMPLIED pysnmpUsmSecretUserName }
+ ::= { pysnmpUsmSecretTable 1 }
+
+PysnmpUsmSecretEntry ::= SEQUENCE {
+ pysnmpUsmSecretUserName SnmpAdminString,
+ pysnmpUsmSecretAuthKey OCTET STRING,
+ pysnmpUsmSecretPrivKey OCTET STRING,
+ pysnmpUsmSecretStatus RowStatus
+}
+
+pysnmpUsmSecretUserName OBJECT-TYPE
+ SYNTAX SnmpAdminString (SIZE(1..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The username string for which a row in this table
+ represents a configuration."
+ ::= { pysnmpUsmSecretEntry 1 }
+
+pysnmpUsmSecretAuthKey OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's authentication passphrase used for localized key generation."
+ ::= { pysnmpUsmSecretEntry 2 }
+
+pysnmpUsmSecretPrivKey OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's encryption passphrase used for localized key generation."
+ ::= { pysnmpUsmSecretEntry 3 }
+
+pysnmpUsmSecretStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Table status"
+ ::= { pysnmpUsmSecretEntry 4 }
+
+--
+-- The pysnmpUsmKeysTable contains a database of USM users' localized
+-- keys.
+--
+
+pysnmpUsmKeyTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PysnmpUsmKeyEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The table of USM users localized keys configured in the
+ SNMP engine's Local Configuration Datastore (LCD)."
+ ::= { pysnmpUsmMIBObjects 3 }
+
+pysnmpUsmKeyEntry OBJECT-TYPE
+ SYNTAX PysnmpUsmKeyEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a particular USM user credentials."
+ AUGMENTS { usmUserEntry }
+ ::= { pysnmpUsmKeyTable 1 }
+
+PysnmpUsmKeyEntry ::= SEQUENCE {
+ pysnmpUsmKeyAuthLocalized OCTET STRING,
+ pysnmpUsmKeyPrivLocalized OCTET STRING,
+ pysnmpUsmKeyAuth OCTET STRING,
+ pysnmpUsmKeyPriv OCTET STRING
+}
+
+pysnmpUsmKeyAuthLocalized OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's localized key used for authentication."
+ ::= { pysnmpUsmKeyEntry 1 }
+
+pysnmpUsmKeyPrivLocalized OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's localized key used for encryption."
+ ::= { pysnmpUsmKeyEntry 2 }
+
+pysnmpUsmKeyAuth OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's non-localized key used for authentication."
+ ::= { pysnmpUsmKeyEntry 3 }
+
+pysnmpUsmKeyPriv OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE(8..32))
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "User's non-localized key used for encryption."
+ ::= { pysnmpUsmKeyEntry 4 }
+
+-- Conformance Information *******************************************
+
+pysnmpUsmMIBCompliances OBJECT IDENTIFIER
+ ::= { pysnmpUsmMIBConformance 1 }
+pysnmpUsmMIBGroups OBJECT IDENTIFIER
+ ::= { pysnmpUsmMIBConformance 2 }
+
+END
diff --git a/pysnmp/proto/secmod/rfc3414/__init__.py b/pysnmp/proto/secmod/rfc3414/__init__.py
new file mode 100644
index 0000000..59df8fb
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/__init__.py
@@ -0,0 +1,3 @@
+from pysnmp.proto.secmod.rfc3414 import service
+
+SnmpUSMSecurityModel = service.SnmpUSMSecurityModel
diff --git a/pysnmp/proto/secmod/rfc3414/auth/__init__.py b/pysnmp/proto/secmod/rfc3414/auth/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/auth/__init__.py
diff --git a/pysnmp/proto/secmod/rfc3414/auth/base.py b/pysnmp/proto/secmod/rfc3414/auth/base.py
new file mode 100644
index 0000000..6572faa
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/auth/base.py
@@ -0,0 +1,8 @@
+class AbstractAuthenticationService:
+ serviceID = None
+ # 7.2.4.1
+ def authenticateOutgoingMsg(self, authKey, wholeMsg):
+ pass
+ # 7.2.4.2
+ def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg):
+ pass
diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py
new file mode 100644
index 0000000..36d0221
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py
@@ -0,0 +1,113 @@
+import string, md5
+from pysnmp.proto.secmod.rfc3414.auth import base
+from pysnmp.proto import error
+
+_twelveZeros = '\x00'*12
+_fortyEightZeros = '\x00'*48
+
+# rfc3414: 6.2.4
+
+class HmacMd5(base.AbstractAuthenticationService):
+ serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 2) # usmHMACMD5AuthProtocol
+ __ipad = [0x36]*64
+ __opad = [0x5C]*64
+
+ # 6.3.1
+ def authenticateOutgoingMsg(self, authKey, wholeMsg):
+ # Here we expect calling secmod to indicate where the digest
+ # should be in the substrate. Also, it pre-sets digest placeholder
+ # so we hash wholeMsg out of the box.
+ # Yes, that's ugly but that's rfc...
+ l = string.find(wholeMsg, _twelveZeros)
+ if l == -1:
+ raise error.ProtocolError('Cant locate digest placeholder')
+ wholeHead = wholeMsg[:l]
+ wholeTail = wholeMsg[l+12:]
+
+ # 6.3.1.1
+
+ # 6.3.1.2a
+ extendedAuthKey = map(ord, str(authKey) + _fortyEightZeros)
+
+ # 6.3.1.2b --> noop
+
+ # 6.3.1.2c
+ k1 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), ''
+ )
+
+ # 6.3.1.2d --> noop
+
+ # 6.3.1.2e
+ k2 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), ''
+ )
+
+ # 6.3.1.3
+ d1 = md5.md5(k1+wholeMsg).digest()
+
+ # 6.3.1.4
+ d2 = md5.md5(k2+d1).digest()
+ mac = d2[:12]
+
+ # 6.3.1.5 & 6
+ return '%s%s%s' % (wholeHead, mac, wholeTail)
+
+ # 6.3.2
+ def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg):
+ # 6.3.2.1 & 2
+ if len(authParameters) != 12:
+ raise error.StatusInformation(
+ errorIndication='authenticationError'
+ )
+
+ # 6.3.2.3
+ l = string.find(wholeMsg, str(authParameters))
+ if l == -1:
+ raise error.ProtocolError('Cant locate digest in wholeMsg')
+ wholeHead = wholeMsg[:l]
+ wholeTail = wholeMsg[l+12:]
+ authenticatedWholeMsg = '%s%s%s' % (
+ wholeHead, _twelveZeros, wholeTail
+ )
+
+ # 6.3.2.4a
+ extendedAuthKey = map(ord, str(authKey) + _fortyEightZeros)
+
+ # 6.3.2.4b --> noop
+
+ # 6.3.2.4c
+ k1 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), ''
+ )
+
+ # 6.3.2.4d --> noop
+
+ # 6.3.2.4e
+ k2 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), ''
+ )
+
+ # 6.3.2.5a
+ d1 = md5.md5(k1+authenticatedWholeMsg).digest()
+
+ # 6.3.2.5b
+ d2 = md5.md5(k2+d1).digest()
+
+ # 6.3.2.5c
+ mac = d2[:12]
+
+ # 6.3.2.6
+ if mac != authParameters:
+ raise error.StatusInformation(
+ errorIndication='authenticationFailure'
+ )
+
+ return authenticatedWholeMsg
+
+# print repr(
+# HmacMd5().authenticateOutgoingMsg('1234567890123456', 'adsd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00afadff')
+# )
+# print repr(
+# HmacMd5().authenticateIncomingMsg('1234567890123456', '\xc2i\x9bu\x92\x96\x00vR\xed$@', 'adsd\xc2i\x9bu\x92\x96\x00vR\xed$@afadff')
+# )
diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py
new file mode 100644
index 0000000..c274257
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py
@@ -0,0 +1,112 @@
+import string, sha
+from pysnmp.proto.secmod.rfc3414.auth import base
+from pysnmp.proto import error
+
+_twelveZeros = '\x00'*12
+_fortyFourZeros = '\x00'*44
+
+# 7.2.4
+
+class HmacSha(base.AbstractAuthenticationService):
+ serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 3) # usmHMACSHAAuthProtocol
+ __ipad = [0x36]*64
+ __opad = [0x5C]*64
+
+ # 7.3.1
+ def authenticateOutgoingMsg(self, authKey, wholeMsg):
+ # 7.3.1.1
+ # Here we expect calling secmod to indicate where the digest
+ # should be in the substrate. Also, it pre-sets digest placeholder
+ # so we hash wholeMsg out of the box.
+ # Yes, that's ugly but that's rfc...
+ l = string.find(wholeMsg, _twelveZeros)
+ if l == -1:
+ raise error.ProtocolError('Cant locate digest placeholder')
+ wholeHead = wholeMsg[:l]
+ wholeTail = wholeMsg[l+12:]
+
+ # 7.3.1.2a
+ extendedAuthKey = map(ord, str(authKey) + _fortyFourZeros)
+
+ # 7.3.1.2b -- noop
+
+ # 7.3.1.2c
+ k1 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), ''
+ )
+
+ # 7.3.1.2d -- noop
+
+ # 7.3.1.2e
+ k2 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), ''
+ )
+
+ # 7.3.1.3
+ d1 = sha.sha(k1+wholeMsg).digest()
+
+ # 7.3.1.4
+ d2 = sha.sha(k2+d1).digest()
+ mac = d2[:12]
+
+ # 7.3.1.5 & 6
+ return '%s%s%s' % (wholeHead, mac, wholeTail)
+
+ # 7.3.2
+ def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg):
+ # 7.3.2.1 & 2
+ if len(authParameters) != 12:
+ raise error.StatusInformation(
+ errorIndication='authenticationError'
+ )
+
+ # 7.3.2.3
+ l = string.find(wholeMsg, str(authParameters))
+ if l == -1:
+ raise error.ProtocolError('Cant locate digest in wholeMsg')
+ wholeHead = wholeMsg[:l]
+ wholeTail = wholeMsg[l+12:]
+ authenticatedWholeMsg = '%s%s%s' % (
+ wholeHead, _twelveZeros, wholeTail
+ )
+
+ # 7.3.2.4a
+ extendedAuthKey = map(ord, str(authKey) + _fortyFourZeros)
+
+ # 7.3.2.4b --> noop
+
+ # 7.3.2.4c
+ k1 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__ipad), ''
+ )
+
+ # 7.3.2.4d --> noop
+
+ # 7.3.2.4e
+ k2 = string.join(
+ map(lambda x,y: chr(x^y), extendedAuthKey, self.__opad), ''
+ )
+
+ # 7.3.2.5a
+ d1 = sha.sha(k1+authenticatedWholeMsg).digest()
+
+ # 7.3.2.5b
+ d2 = sha.sha(k2+d1).digest()
+
+ # 7.3.2.5c
+ mac = d2[:12]
+
+ # 7.3.2.6
+ if mac != authParameters:
+ raise error.StatusInformation(
+ errorIndication='authenticationFailure'
+ )
+
+ return authenticatedWholeMsg
+
+# print repr(
+# HmacSha().authenticateOutgoingMsg('12345678901234567890', 'adsd\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00afadff')
+# )
+# print repr(
+# HmacSha().authenticateIncomingMsg('12345678901234567890', '0\x9c\x98g\xdb\xfd\xcd\xb0\xf9y\xc7b', 'adsd0\x9c\x98g\xdb\xfd\xcd\xb0\xf9y\xc7bafadff')
+# )
diff --git a/pysnmp/proto/secmod/rfc3414/localkey.py b/pysnmp/proto/secmod/rfc3414/localkey.py
new file mode 100644
index 0000000..a5b923f
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/localkey.py
@@ -0,0 +1,38 @@
+# Convert plaintext passphrase into a localized key
+import md5
+
+# RFC3414: A.2.1
+def hashPassphrase(passphrase):
+ md = md5.new()
+ passLen = len(passphrase)
+ count = passIndex = 0
+ while count < 1048575: # why rfc says 1048576?
+ i = 0; passBuf = ''
+ while i < 64:
+ passBuf = passBuf + passphrase[passIndex % passLen]
+ i = i + 1; passIndex = passIndex + 1;
+ md.update(passBuf)
+ count = count + 64
+ return md.digest()
+
+def localizeKey(passKey, snmpEngineId):
+ return md5.new('%s%s%s' % (passKey, str(snmpEngineId), passKey)).digest()
+
+def passwordToKeyMD5(passphrase, snmpEngineId):
+ return localizeKey(hashPassphrase(passphrase), snmpEngineId)
+
+# XXX
+# d, m = divmod(len(password))
+# prevM = count = 0
+# while count < 1048576:
+# i = 0
+# while i < 64:
+# md.update(password[prevM:] + password * (d-1) + \
+# password[:prevM] + password[prevM:prevM+m])
+# prevM = m
+
+#print map(lambda x: '%x' % ord(x), passwordToKeyMD5('maplesyrup', '\x00'*11+'\x02'))
+import string
+
+#print repr(passwordToKeyMD5('12345678', '\x80\x00\x07\xe5\x80<\x93eE\xdb_\x88B'))
+#print string.join(map(lambda x: x, passwordToKeyMD5('12345678', '\x80\x00\x07\xe5\x80<\x93eE\xdb_\x88B')), '')
diff --git a/pysnmp/proto/secmod/rfc3414/priv/__init__.py b/pysnmp/proto/secmod/rfc3414/priv/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/priv/__init__.py
diff --git a/pysnmp/proto/secmod/rfc3414/priv/base.py b/pysnmp/proto/secmod/rfc3414/priv/base.py
new file mode 100644
index 0000000..8690d4d
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/priv/base.py
@@ -0,0 +1,4 @@
+class AbstractEncryptionService:
+ serviceID = None
+ def encryptData(self, encryptKey, dataToEncrypt): pass
+ def decryptData(self, decryptKey, privParameters, encryptedData): pass
diff --git a/pysnmp/proto/secmod/rfc3414/priv/des.py b/pysnmp/proto/secmod/rfc3414/priv/des.py
new file mode 100644
index 0000000..aead8cd
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/priv/des.py
@@ -0,0 +1,117 @@
+import random, string
+from pysnmp.proto.secmod.rfc3414.priv import base
+from pyasn1.type import univ
+from pysnmp.proto import error
+
+try:
+ from Crypto.Cipher import DES
+except ImportError:
+ DES = None
+
+random.seed()
+
+# 8.2.4
+
+class Des(base.AbstractEncryptionService):
+ serviceID = (1, 3, 6, 1, 6, 3, 10, 1, 2, 2) # usmDESPrivProtocol
+ _localInt = long(random.random()*0xffffffff)
+ # 8.1.1.1
+ def __getEncryptionKey(self, mibInstrumController, privKey):
+ desKey = privKey[:8]
+ preIV = privKey[8:16]
+
+ snmpEngineBoots, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-FRAMEWORK-MIB', 'snmpEngineBoots'
+ )
+ securityEngineBoots = long(snmpEngineBoots.syntax)
+
+ salt = [
+ securityEngineBoots>>24&0xff,
+ securityEngineBoots>>16&0xff,
+ securityEngineBoots>>8&0xff,
+ securityEngineBoots&0xff,
+ self._localInt>>24&0xff,
+ self._localInt>>16&0xff,
+ self._localInt>>8&0xff,
+ self._localInt&0xff
+ ]
+ if self._localInt == 0xffffffff:
+ self._localInt = 0
+ else:
+ self._localInt = self._localInt + 1
+
+ return desKey, \
+ string.join(map(lambda x: chr(x), salt), ''), \
+ string.join(map(lambda x,y: chr(x^ord(y)), salt, preIV), '')
+
+ def __getDecryptionKey(self, mibInstrumController, privKey, salt):
+ return privKey[:8], string.join(
+ map(lambda x,y: chr(ord(x)^ord(y)), salt, privKey[8:16]), ''
+ )
+
+ # 8.2.4.1
+ def encryptData(self, mibInstrumController, encryptKey, dataToEncrypt):
+ if DES is None:
+ raise error.StatusInformation(
+ errorIndication='encryptionError'
+ )
+
+ # 8.3.1.1
+ desKey, salt, iv = self.__getEncryptionKey(
+ mibInstrumController, str(encryptKey)
+ )
+
+ # 8.3.1.2
+ privParameters = univ.OctetString(salt)
+
+ # 8.1.1.2
+ desObj = DES.new(desKey, DES.MODE_CBC, iv) # XXX
+ plaintext = dataToEncrypt + '\x00' * (8 - len(dataToEncrypt) % 8)
+ ciphertext = desObj.encrypt(plaintext)
+
+ # 8.3.1.3 & 4
+ return univ.OctetString(ciphertext), privParameters
+
+ # 8.2.4.2
+ def decryptData(self, mibInstrumController, decryptKey,
+ privParameters, encryptedData):
+ if DES is None:
+ raise error.StatusInformation(
+ errorIndication='decryptionError'
+ )
+
+ # 8.3.2.1
+ if len(privParameters) != 8:
+ raise error.StatusInformation(
+ errorIndication='decryptionError'
+ )
+
+ # 8.3.2.2
+ salt = str(privParameters)
+
+ # 8.3.2.3
+ desKey, iv = self.__getDecryptionKey(
+ mibInstrumController, str(decryptKey), salt
+ )
+
+ # 8.3.2.4 -> 8.1.1.3
+ if len(encryptedData) % 8 != 0:
+ raise error.StatusInformation(
+ errorIndication='decryptionError'
+ )
+
+ desObj = DES.new(desKey, DES.MODE_CBC, iv)
+
+ # 8.3.2.6
+ return desObj.decrypt(str(encryptedData))
+
+if __name__ == '__main__':
+ from pysnmp.smi import builder, instrum
+
+ mibInstrumController = instrum.MibInstrumController(
+ builder.MibBuilder()
+ )
+
+ d = Des()
+ ct, p = d.encryptData(mibInstrumController, '1234567890123456', 'security? not my problem!')
+ print d.decryptData(mibInstrumController, '1234567890123456', p, ct)
diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py
new file mode 100644
index 0000000..adf05bb
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc3414/service.py
@@ -0,0 +1,689 @@
+# SNMP v3 USM model services
+from pysnmp.proto.secmod.base import AbstractSecurityModel
+from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
+from pysnmp.proto.secmod.rfc3414.priv import des
+from pysnmp.proto.secmod.rfc3414 import localkey
+from pysnmp.smi.error import NoSuchInstanceError
+from pysnmp.proto import error
+from pyasn1.type import univ, namedtype, constraint
+from pyasn1.codec.ber import encoder, decoder
+from pyasn1.error import PyAsn1Error
+
+# USM security params
+
+class UsmSecurityParameters(univ.Sequence):
+ componentType = namedtype.NamedTypes(
+ namedtype.NamedType('msgAuthoritativeEngineID', univ.OctetString()),
+ namedtype.NamedType('msgAuthoritativeEngineBoots', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
+ namedtype.NamedType('msgAuthoritativeEngineTime', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 2147483647))),
+ namedtype.NamedType('msgUserName', univ.OctetString().subtype(subtypeSpec=constraint.ValueSizeConstraint(0, 32))),
+ namedtype.NamedType('msgAuthenticationParameters', univ.OctetString()),
+ namedtype.NamedType('msgPrivacyParameters', univ.OctetString())
+ )
+
+class SnmpUSMSecurityModel(AbstractSecurityModel):
+ securityModelID = 3
+ authServices = {
+ hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
+ hmacsha.HmacSha.serviceID: hmacsha.HmacSha()
+ }
+ privServices = {
+ des.Des.serviceID: des.Des()
+ }
+ _securityParametersSpec = UsmSecurityParameters()
+ def __init__(self):
+ AbstractSecurityModel.__init__(self)
+ self.__timeline = {}
+
+ def __getUserInfo(
+ self, mibInstrumController, securityEngineID, securityName
+ ):
+ usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
+ )
+ tblIdx = usmUserEntry.getInstIdFromIndices(
+ securityEngineID, securityName
+ )
+ # Get protocols
+ usmUserSecurityName = usmUserEntry.getNode(
+ usmUserEntry.name + (3,) + tblIdx
+ ).syntax
+ usmUserAuthProtocol = usmUserEntry.getNode(
+ usmUserEntry.name + (5,) + tblIdx
+ ).syntax
+ usmUserPrivProtocol = usmUserEntry.getNode(
+ usmUserEntry.name + (8,) + tblIdx
+ ).syntax
+ # Get keys
+ pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
+ )
+ pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (1,) + tblIdx
+ ).syntax
+ pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (2,) + tblIdx
+ ).syntax
+ return (
+ usmUserSecurityName, # XXX function needed?
+ usmUserAuthProtocol,
+ pysnmpUsmKeyAuthLocalized,
+ usmUserPrivProtocol,
+ pysnmpUsmKeyPrivLocalized
+ )
+
+ def __cloneUserInfo(
+ self, mibInstrumController, securityEngineID, securityName
+ ):
+ snmpEngineID, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-FRAMEWORK-MIB', 'snmpEngineID'
+ )
+ # Proto entry
+ usmUserEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-USER-BASED-SM-MIB', 'usmUserEntry'
+ )
+ tblIdx = usmUserEntry.getInstIdFromIndices(
+ snmpEngineID.syntax, securityName
+ )
+ # Get proto protocols
+ usmUserSecurityName = usmUserEntry.getNode(
+ usmUserEntry.name + (3,) + tblIdx
+ )
+ usmUserAuthProtocol = usmUserEntry.getNode(
+ usmUserEntry.name + (5,) + tblIdx
+ )
+ usmUserPrivProtocol = usmUserEntry.getNode(
+ usmUserEntry.name + (8,) + tblIdx
+ )
+ # Get proto keys
+ pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
+ )
+ pysnmpUsmKeyAuth = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (3,) + tblIdx
+ )
+ pysnmpUsmKeyPriv = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (4,) + tblIdx
+ )
+
+ # Create new row from proto values
+
+ tblIdx = usmUserEntry.getInstIdFromIndices(
+ securityEngineID, securityName
+ )
+ # New row
+ mibInstrumController.writeVars(
+ ((usmUserEntry.name + (13,) + tblIdx, 4),)
+ )
+ # Set protocols
+ usmUserEntry.getNode(
+ usmUserEntry.name + (3,) + tblIdx
+ ).syntax = usmUserSecurityName.syntax
+ usmUserEntry.getNode(
+ usmUserEntry.name + (5,) + tblIdx
+ ).syntax = usmUserAuthProtocol.syntax
+ usmUserEntry.getNode(
+ usmUserEntry.name + (8,) + tblIdx
+ ).syntax = usmUserPrivProtocol.syntax
+
+ # Localize and set keys
+ pysnmpUsmKeyEntry, = mibInstrumController.mibBuilder.importSymbols(
+ 'PYSNMP-USM-MIB', 'pysnmpUsmKeyEntry'
+ )
+ pysnmpUsmKeyAuthLocalized = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (1,) + tblIdx
+ )
+ pysnmpUsmKeyAuthLocalized.syntax = pysnmpUsmKeyAuthLocalized.syntax.clone(localkey.localizeKey(pysnmpUsmKeyAuth.syntax, securityEngineID))
+ pysnmpUsmKeyPrivLocalized = pysnmpUsmKeyEntry.getNode(
+ pysnmpUsmKeyEntry.name + (2,) + tblIdx
+ )
+ pysnmpUsmKeyPrivLocalized.syntax = pysnmpUsmKeyPrivLocalized.syntax.clone(localkey.localizeKey(pysnmpUsmKeyPriv.syntax, securityEngineID))
+ return (
+ usmUserSecurityName.syntax, # XXX function needed?
+ usmUserAuthProtocol.syntax,
+ pysnmpUsmKeyAuthLocalized.syntax,
+ usmUserPrivProtocol.syntax,
+ pysnmpUsmKeyPrivLocalized.syntax
+ )
+
+ def __generateRequestOrResponseMsg(
+ self,
+ snmpEngine,
+ messageProcessingModel,
+ globalData,
+ maxMessageSize,
+ securityModel,
+ securityEngineID,
+ securityName,
+ securityLevel,
+ scopedPDU,
+ securityStateReference
+ ):
+ snmpEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
+ # 3.1.1
+ if securityStateReference is not None:
+ # 3.1.1a
+ cachedSecurityData = self._cachePop(securityStateReference)
+ usmUserName = cachedSecurityData['msgUserName']
+ usmUserAuthProtocol = cachedSecurityData.get('usmUserAuthProtocol')
+ usmUserAuthKeyLocalized = cachedSecurityData.get(
+ 'usmUserAuthKeyLocalized'
+ )
+ usmUserPrivProtocol = cachedSecurityData.get('usmUserPrivProtocol')
+ usmUserPrivKeyLocalized = cachedSecurityData.get(
+ 'usmUserPrivKeyLocalized'
+ )
+ securityEngineID = snmpEngineID
+ elif securityName:
+ # 3.1.1b
+ try:
+ ( usmUserName,
+ usmUserAuthProtocol,
+ usmUserAuthKeyLocalized,
+ usmUserPrivProtocol,
+ usmUserPrivKeyLocalized ) = self.__getUserInfo(
+ snmpEngine.msgAndPduDsp.mibInstrumController, securityEngineID, securityName
+ )
+ except NoSuchInstanceError:
+ pysnmpUsmDiscovery, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscovery')
+ __reportUnknownName = not pysnmpUsmDiscovery.syntax
+ if not __reportUnknownName:
+ try:
+ ( usmUserName,
+ usmUserAuthProtocol,
+ usmUserAuthKeyLocalized,
+ usmUserPrivProtocol,
+ usmUserPrivKeyLocalized ) = self.__cloneUserInfo(
+ snmpEngine.msgAndPduDsp.mibInstrumController,securityEngineID, securityName
+ )
+ except NoSuchInstanceError:
+ __reportUnknownName = 1
+ if __reportUnknownName:
+ raise error.StatusInformation(
+ errorIndication = 'unknownSecurityName'
+ )
+ else:
+ # empty username used for engineID discovery
+ usmUserName = usmUserSecurityName = ''
+ usmUserAuthProtocol = usmUserAuthKeyLocalized = None
+ usmUserPrivProtocol = usmUserPrivKeyLocalized = None
+
+ msg = globalData
+
+ # 3.1.2
+ if securityLevel == 3:
+ if not usmUserAuthProtocol or not usmUserPrivProtocol:
+ raise error.StatusInformation(
+ errorIndication = 'unsupportedSecurityLevel'
+ )
+
+ # 3.1.3
+ if securityLevel == 3 or securityLevel == 2:
+ if not usmUserAuthProtocol:
+ raise error.StatusInformation(
+ errorIndication = 'unsupportedSecurityLevel'
+ )
+
+ securityParameters = UsmSecurityParameters()
+
+ scopedPDUData = msg.setComponentByPosition(3).getComponentByPosition(3)
+ scopedPDUData.setComponentByPosition(0, scopedPDU)
+
+ # 3.1.4a
+ if securityLevel == 3:
+ privHandler = self.privServices.get(
+ tuple(usmUserPrivProtocol)
+ )
+ if privHandler is None:
+ raise error.StatusInformation(
+ errorIndication = 'encryptionError'
+ )
+ dataToEncrypt = encoder.encode(scopedPDU)
+ try:
+ ( encryptedData,
+ privParameters ) = privHandler.encryptData(
+ snmpEngine.msgAndPduDsp.mibInstrumController, usmUserPrivKeyLocalized, dataToEncrypt
+ )
+ except error.StatusInformation, statusInformation:
+ raise
+
+ securityParameters.setComponentByPosition(5, privParameters)
+ scopedPDUData.setComponentByPosition(1, encryptedData)
+
+ # 3.1.4b
+ elif securityLevel == 1 or securityLevel == 2:
+ securityParameters.setComponentByPosition(5, '')
+
+ # 3.1.5
+ securityParameters.setComponentByPosition(0, str(securityEngineID))
+
+ # 3.1.6a
+ if securityStateReference is None and ( # request type check added
+ securityLevel == 3 or securityLevel == 2
+ ):
+ if self.__timeline.has_key(securityEngineID):
+ ( snmpEngineBoots,
+ snmpEngineTime,
+ latestReceivedEngineTime ) = self.__timeline[
+ securityEngineID
+ ]
+ else:
+ # 2.3 XXX is this correct?
+ snmpEngineBoots = snmpEngineTime = 0
+ # 3.1.6.b
+ elif securityStateReference is not None: # XXX Report?
+ ( snmpEngineBoots,
+ snmpEngineTime ) = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineBoots', 'snmpEngineTime')
+ snmpEngineBoots = snmpEngineBoots.syntax
+ snmpEngineTime = snmpEngineTime.syntax
+ # 3.1.6.c
+ else:
+ snmpEngineBoots = snmpEngineTime = 0
+
+ securityParameters.setComponentByPosition(1, int(snmpEngineBoots))
+ securityParameters.setComponentByPosition(2, int(snmpEngineTime))
+
+ # 3.1.7
+ securityParameters.setComponentByPosition(3, str(usmUserName))
+
+ # 3.1.8a
+ if securityLevel == 3 or securityLevel == 2:
+ authHandler = self.authServices.get(
+ tuple(usmUserAuthProtocol)
+ )
+ if authHandler is None:
+ raise error.StatusInformation(
+ errorIndication = 'authenticationFailure'
+ )
+
+ # extra-wild hack to facilitate BER substrate in-place re-write
+ securityParameters.setComponentByPosition(
+ 4, '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ )
+
+ msg.setComponentByPosition(2, encoder.encode(securityParameters))
+
+ wholeMsg = encoder.encode(msg)
+
+ try:
+ authenticatedWholeMsg = authHandler.authenticateOutgoingMsg(
+ usmUserAuthKeyLocalized, wholeMsg
+ )
+ except error.StatusInformation, statusInformation:
+ raise
+
+ # 3.1.8b
+ else:
+ securityParameters.setComponentByPosition(4, '')
+ msg.setComponentByPosition(2, encoder.encode(securityParameters))
+ authenticatedWholeMsg = encoder.encode(msg)
+
+ # 3.1.9
+ return (
+ msg.getComponentByPosition(2),
+ authenticatedWholeMsg
+ )
+
+ def generateRequestMsg(
+ self,
+ snmpEngine,
+ messageProcessingModel,
+ globalData,
+ maxMessageSize,
+ securityModel,
+ securityEngineID,
+ securityName,
+ securityLevel,
+ scopedPDU,
+ ):
+ return self.__generateRequestOrResponseMsg(
+ snmpEngine,
+ messageProcessingModel,
+ globalData,
+ maxMessageSize,
+ securityModel,
+ securityEngineID,
+ securityName,
+ securityLevel,
+ scopedPDU,
+ None
+ )
+
+ def generateResponseMsg(
+ self,
+ snmpEngine,
+ messageProcessingModel,
+ globalData,
+ maxMessageSize,
+ securityModel,
+ securityEngineID,
+ securityName,
+ securityLevel,
+ scopedPDU,
+ securityStateReference
+ ):
+ return self.__generateRequestOrResponseMsg(
+ snmpEngine,
+ messageProcessingModel,
+ globalData,
+ maxMessageSize,
+ securityModel,
+ securityEngineID,
+ securityName,
+ securityLevel,
+ scopedPDU,
+ securityStateReference
+ )
+
+ # 3.2
+ def processIncomingMsg(
+ self,
+ snmpEngine,
+ messageProcessingModel,
+ maxMessageSize,
+ securityParameters,
+ securityModel,
+ securityLevel,
+ wholeMsg,
+ msg # XXX
+ ):
+ # 3.2.1
+ try:
+ securityParameters, rest = decoder.decode(
+ securityParameters,
+ asn1Spec=self._securityParametersSpec
+ )
+ except PyAsn1Error, why:
+ snmpInASNParseErrs, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMPv2-MIB', 'snmpInASNParseErrs')
+ snmpInASNParseErrs.syntax = snmpInASNParseErrs.syntax + 1
+ raise error.StatusInformation(
+ errorIndication='parseError'
+ )
+
+ # 3.2.9 -- moved up here to be able to report
+ # maxSizeResponseScopedPDU on error
+ maxSizeResponseScopedPDU = maxMessageSize - 512 # XXX
+ if maxSizeResponseScopedPDU < 0:
+ maxSizeResponseScopedPDU = 0
+
+ # 3.2.2
+ securityEngineID = securityParameters.getComponentByPosition(0)
+ securityStateReference = self._cachePush(
+ msgUserName=securityParameters.getComponentByPosition(3)
+ )
+
+ # Used for error reporting
+ contextEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-FRAMEWORK-MIB', 'snmpEngineID')[0].syntax
+ contextName = ''
+
+ # 3.2.3
+ if not self.__timeline.has_key(securityEngineID):
+ if securityEngineID:
+ # 3.2.3a XXX any other way to get auth engine in cache?
+ self.__timeline[securityEngineID] = (
+ securityParameters.getComponentByPosition(1),
+ securityParameters.getComponentByPosition(2),
+ securityParameters.getComponentByPosition(2)
+ )
+ else:
+ # 3.2.3b
+ usmStatsUnknownEngineIDs, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownEngineIDs')
+ usmStatsUnknownEngineIDs.syntax = usmStatsUnknownEngineIDs.syntax+1
+ pysnmpUsmDiscoverable, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable')
+ if pysnmpUsmDiscoverable.syntax:
+ raise error.StatusInformation(
+ errorIndication = 'unknownEngineID',
+ oid=usmStatsUnknownEngineIDs.name,
+ val=usmStatsUnknownEngineIDs.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+ else:
+ # free securityStateReference XXX
+ raise error.StatusInformation(
+ errorIndication = 'unknownEngineID'
+ )
+
+ snmpEngineID = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols(
+ 'SNMP-FRAMEWORK-MIB', 'snmpEngineID'
+ )[0].syntax
+
+ msgAuthoritativeEngineID = securityParameters.getComponentByPosition(0)
+ msgUserName = securityParameters.getComponentByPosition(3)
+
+ if msgUserName:
+ # 3.2.4
+ try:
+ ( usmUserSecurityName,
+ usmUserAuthProtocol,
+ usmUserAuthKeyLocalized,
+ usmUserPrivProtocol,
+ usmUserPrivKeyLocalized ) = self.__getUserInfo(
+ snmpEngine.msgAndPduDsp.mibInstrumController, msgAuthoritativeEngineID, msgUserName
+ )
+ except NoSuchInstanceError:
+ pysnmpUsmDiscoverable, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('PYSNMP-USM-MIB', 'pysnmpUsmDiscoverable')
+ __reportUnknownName = not pysnmpUsmDiscoverable.syntax
+ if not __reportUnknownName:
+ try:
+ ( usmUserSecurityName,
+ usmUserAuthProtocol,
+ usmUserAuthKeyLocalized,
+ usmUserPrivProtocol,
+ usmUserPrivKeyLocalized ) = self.__cloneUserInfo(
+ snmpEngine.msgAndPduDsp.mibInstrumController,securityEngineID, msgUserName
+ )
+ except NoSuchInstanceError:
+ __reportUnknownName = 1
+ if __reportUnknownName:
+ usmStatsUnknownUserNames, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
+ usmStatsUnknownUserNames.syntax = usmStatsUnknownUserNames.syntax+1
+ raise error.StatusInformation(
+ errorIndication = 'unknownSecurityName',
+ oid = usmStatsUnknownUserNames.name,
+ val = usmStatsUnknownUserNames.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+ else:
+ # empty username used for engineID discovery
+ usmUserName = usmUserSecurityName = ''
+ usmUserAuthProtocol = usmUserAuthKeyLocalized = None
+ usmUserPrivProtocol = usmUserPrivKeyLocalized = None
+
+ # 3.2.5
+ __reportError = 0
+ if securityLevel == 3:
+ if not usmUserAuthProtocol or not usmUserPrivProtocol:
+ __reportError = 1
+ elif securityLevel == 2:
+ if not usmUserAuthProtocol:
+ __reportError = 1
+ if __reportError:
+ usmStatsUnsupportedSecLevels, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnsupportedSecLevels')
+ usmStatsUnsupportedSecLevels.syntax = usmStatsUnsupportedSecLevels.syntax + 1
+ raise error.StatusInformation(
+ errorIndication='unsupportedSecurityLevel',
+ oid=usmStatsUnknownEngineIDs.name,
+ val=usmStatsUnknownEngineIDs.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+
+ # 3.2.6
+ if securityLevel == 3 or securityLevel == 2:
+ authHandler = self.authServices.get(
+ tuple(usmUserAuthProtocol)
+ )
+ if authHandler is None:
+ raise error.StatusInformation(
+ errorIndication = 'authenticationFailure'
+ )
+ try:
+ authenticatedWholeMsg = authHandler.authenticateIncomingMsg(
+ usmUserAuthKeyLocalized,
+ securityParameters.getComponentByPosition(4),
+ wholeMsg
+ )
+ except error.StatusInformation:
+ usmStatsWrongDigests, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsWrongDigests')
+ usmStatsWrongDigests.syntax = usmStatsWrongDigests.syntax+1
+ raise error.StatusInformation(
+ errorIndication = 'authenticationFailure',
+ oid=usmStatsWrongDigests.name,
+ val=usmStatsWrongDigests.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+
+ # 3.2.7
+ if securityLevel == 3 or securityLevel == 2:
+ if self.__timeline.has_key(securityEngineID):
+ ( snmpEngineBoots,
+ snmpEngineTime,
+ latestReceivedEngineTime ) = self.__timeline[
+ msgAuthoritativeEngineID
+ ]
+ else:
+ raise error.ProtocolError('Peer SNMP engine info missing')
+
+ msgAuthoritativeEngineBoots = securityParameters.getComponentByPosition(1)
+ msgAuthoritativeEngineTime = securityParameters.getComponentByPosition(2)
+
+ # 3.2.7a
+ if msgAuthoritativeEngineID == snmpEngineID:
+ if snmpEngineBoots == 2147483647 or \
+ snmpEngineBoots != msgAuthoritativeEngineBoots or \
+ abs(int(snmpEngineTime)-int(msgAuthoritativeEngineTime)) > 150:
+ usmStatsNotInTimeWindows, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsNotInTimeWindows')
+ usmStatsNotInTimeWindows.syntax = usmStatsNotInTimeWindows.syntax+1
+ raise error.StatusInformation(
+ errorIndication = 'notInTimeWindow',
+ oid=usmStatsNotInTimeWindows.name,
+ val=usmStatsNotInTimeWindows.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=2,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+ # 3.2.7b
+ else:
+ # 3.2.7b.1
+ if msgAuthoritativeEngineBoots > snmpEngineBoots or \
+ msgAuthoritativeEngineBoots == snmpEngineBoots and \
+ msgAuthoritativeEngineTime > latestReceivedEngineTime:
+ self.__timeline[msgAuthoritativeEngineID] = (
+ msgAuthoritativeEngineBoots,
+ msgAuthoritativeEngineTime,
+ msgAuthoritativeEngineTime
+ )
+
+ # 3.2.7b.2
+ if snmpEngineBoots == 2147483647 or \
+ msgAuthoritativeEngineBoots < snmpEngineBoots or \
+ msgAuthoritativeEngineBoots == snmpEngineBoots and \
+ msgAuthoritativeEngineTime - snmpEngineTime < -150:
+ raise error.StatusInformation(
+ errorIndication = 'notInTimeWindow'
+ )
+
+ scopedPduData = msg.getComponentByPosition(3)
+
+ # 3.2.8a
+ if securityLevel == 3:
+ privHandler = self.privServices.get(
+ tuple(usmUserPrivProtocol)
+ )
+ if privHandler is None:
+ raise error.StatusInformation(
+ errorIndication = 'decryptionError'
+ )
+ encryptedPDU = scopedPduData.getComponentByPosition(1)
+ if encryptedPDU is None: # no ciphertext
+ raise error.StatusInformation(
+ errorIndication = 'decryptionError'
+ )
+ try:
+ decryptedData = privHandler.decryptData(
+ snmpEngine.msgAndPduDsp.mibInstrumController, usmUserPrivKeyLocalized,
+ securityParameters.getComponentByPosition(5),
+ encryptedPDU
+ )
+ except error.StatusInformation:
+ usmStatsDecryptionErrors, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsDecryptionErrors')
+ usmStatsDecryptionErrors.syntax = usmStatsDecryptionErrors.syntax+1
+ raise error.StatusInformation(
+ errorIndication = 'decryptionError',
+ oid=usmStatsDecryptionErrors.name,
+ val=usmStatsDecryptionErrors.syntax,
+ securityStateReference=securityStateReference,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU
+ )
+ scopedPduSpec = scopedPduData.setComponentByPosition(0).getComponentByPosition(0)
+ scopedPDU, rest = decoder.decode(
+ decryptedData, asn1Spec=scopedPduSpec
+ )
+ else:
+ # 3.2.8b
+ scopedPDU = scopedPduData.getComponentByPosition(0)
+ if scopedPDU is None: # no plaintext
+ raise error.StatusInformation(
+ errorIndication = 'decryptionError'
+ )
+
+ # 3.2.10
+ securityName = usmUserSecurityName
+
+ # 3.2.11
+ self._cachePop(securityStateReference)
+ securityStateReference = self._cachePush(
+ msgUserName=securityParameters.getComponentByPosition(3),
+ usmUserAuthProtocol=usmUserAuthProtocol,
+ usmUserAuthKeyLocalized=usmUserAuthKeyLocalized,
+ usmUserPrivProtocol=usmUserPrivProtocol,
+ usmUserPrivKeyLocalized=usmUserPrivKeyLocalized
+ )
+
+ # Delayed to include details
+ if not msgUserName and not securityEngineID:
+ usmStatsUnknownUserNames, = snmpEngine.msgAndPduDsp.mibInstrumController.mibBuilder.importSymbols('SNMP-USER-BASED-SM-MIB', 'usmStatsUnknownUserNames')
+ usmStatsUnknownUserNames.syntax = usmStatsUnknownUserNames.syntax+1
+ raise error.StatusInformation(
+ errorIndication='unknownSecurityName',
+ oid=usmStatsUnknownUserNames.name,
+ val=usmStatsUnknownUserNames.syntax,
+ securityStateReference=securityStateReference,
+ securityEngineID=securityEngineID,
+ securityLevel=securityLevel,
+ contextEngineID=contextEngineID,
+ contextName=contextName,
+ maxSizeResponseScopedPDU=maxSizeResponseScopedPDU,
+ PDU=scopedPDU
+ )
+
+ # 3.2.12
+ return ( securityEngineID,
+ securityName,
+ scopedPDU,
+ maxSizeResponseScopedPDU,
+ securityStateReference )
+
+# XXX
+# get rid of msg handler passed into processIncomingMsg
+# usmUserName vs usmUserSecurityName -- map?
diff --git a/pysnmp/smi/exval.py b/pysnmp/smi/exval.py
new file mode 100644
index 0000000..3676330
--- /dev/null
+++ b/pysnmp/smi/exval.py
@@ -0,0 +1,6 @@
+from pyasn1.type import univ, tag
+
+noSuchInstance = univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0x00))
+noSuchObject = univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0x01))
+endOfMib = univ.Null().subtype(implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0x02))
+