add support for USM SHA-2 algorithms (RFC 7860) (#71)
parent
801d47b632
commit
130bdaa6e2
|
@ -13,3 +13,7 @@ docs/source/.templates/
|
|||
|
||||
# PyCharm stuff
|
||||
.idea/
|
||||
|
||||
# Eclipse stuff
|
||||
.project
|
||||
.pydevproject
|
||||
|
|
|
@ -32,7 +32,7 @@ Features
|
|||
|
||||
Features, specific to SNMPv3 model include:
|
||||
|
||||
* USM authentication (MD5/SHA) and privacy (DES/AES) protocols (RFC3414)
|
||||
* USM authentication (MD5/SHA-1/SHA-2) and privacy (DES/AES) protocols (RFC3414, RFC7860)
|
||||
* View-based access control to use with any SNMP model (RFC3415)
|
||||
* Built-in SNMP proxy PDU converter for building multi-lingual
|
||||
SNMP entities (RFC2576)
|
||||
|
|
|
@ -21,6 +21,8 @@ pysnmpUsmMIB MODULE-IDENTITY
|
|||
DESCRIPTION
|
||||
"This MIB module defines objects specific to User
|
||||
Security Model (USM) implementation at PySNMP."
|
||||
REVISION "201707300000Z"
|
||||
DESCRIPTION "Extended authentication key size"
|
||||
REVISION "201704140000Z"
|
||||
DESCRIPTION "Updated addresses"
|
||||
REVISION "200505140000Z" -- 14 May 2005, midnight
|
||||
|
@ -153,7 +155,7 @@ PysnmpUsmKeyEntry ::= SEQUENCE {
|
|||
}
|
||||
|
||||
pysnmpUsmKeyAuthLocalized OBJECT-TYPE
|
||||
SYNTAX OCTET STRING (SIZE(8..32))
|
||||
SYNTAX OCTET STRING (SIZE(8..64))
|
||||
MAX-ACCESS not-accessible
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
|
@ -161,7 +163,7 @@ pysnmpUsmKeyAuthLocalized OBJECT-TYPE
|
|||
::= { pysnmpUsmKeyEntry 1 }
|
||||
|
||||
pysnmpUsmKeyPrivLocalized OBJECT-TYPE
|
||||
SYNTAX OCTET STRING (SIZE(8..32))
|
||||
SYNTAX OCTET STRING (SIZE(8..64))
|
||||
MAX-ACCESS not-accessible
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
|
@ -169,7 +171,7 @@ pysnmpUsmKeyPrivLocalized OBJECT-TYPE
|
|||
::= { pysnmpUsmKeyEntry 2 }
|
||||
|
||||
pysnmpUsmKeyAuth OBJECT-TYPE
|
||||
SYNTAX OCTET STRING (SIZE(8..32))
|
||||
SYNTAX OCTET STRING (SIZE(8..64))
|
||||
MAX-ACCESS not-accessible
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
|
@ -177,7 +179,7 @@ pysnmpUsmKeyAuth OBJECT-TYPE
|
|||
::= { pysnmpUsmKeyEntry 3 }
|
||||
|
||||
pysnmpUsmKeyPriv OBJECT-TYPE
|
||||
SYNTAX OCTET STRING (SIZE(8..32))
|
||||
SYNTAX OCTET STRING (SIZE(8..64))
|
||||
MAX-ACCESS not-accessible
|
||||
STATUS current
|
||||
DESCRIPTION
|
||||
|
|
|
@ -225,6 +225,10 @@ via constant OIDs:
|
|||
.. autodata:: pysnmp.hlapi.usmNoAuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMACMD5AuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMACSHAAuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMAC128SHA224AuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMAC192SHA256AuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMAC256SHA384AuthProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmHMAC384SHA512AuthProtocol
|
||||
|
||||
.. autodata:: pysnmp.hlapi.usmNoPrivProtocol
|
||||
.. autodata:: pysnmp.hlapi.usmDESPrivProtocol
|
||||
|
|
|
@ -9,6 +9,7 @@ from pysnmp.carrier.asyncore.dgram import udp, udp6, unix
|
|||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha, noauth
|
||||
from pysnmp.proto.secmod.rfc3414.priv import des, nopriv
|
||||
from pysnmp.proto.secmod.rfc3826.priv import aes
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256
|
||||
from pysnmp.proto import rfc1905
|
||||
from pysnmp import error
|
||||
|
@ -23,6 +24,10 @@ snmpLocalDomain = unix.snmpLocalDomain
|
|||
# Auth protocol
|
||||
usmHMACMD5AuthProtocol = hmacmd5.HmacMd5.serviceID
|
||||
usmHMACSHAAuthProtocol = hmacsha.HmacSha.serviceID
|
||||
usmHMAC128SHA224AuthProtocol = hmacsha2.HmacSha2.sha224ServiceID
|
||||
usmHMAC192SHA256AuthProtocol = hmacsha2.HmacSha2.sha256ServiceID
|
||||
usmHMAC256SHA384AuthProtocol = hmacsha2.HmacSha2.sha384ServiceID
|
||||
usmHMAC384SHA512AuthProtocol = hmacsha2.HmacSha2.sha512ServiceID
|
||||
usmNoAuthProtocol = noauth.NoAuth.serviceID
|
||||
|
||||
# Privacy protocol
|
||||
|
@ -38,6 +43,10 @@ usmNoPrivProtocol = nopriv.NoPriv.serviceID
|
|||
# Auth services
|
||||
authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
|
||||
hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
|
||||
hmacsha2.HmacSha2.sha224ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha224ServiceID),
|
||||
hmacsha2.HmacSha2.sha256ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha256ServiceID),
|
||||
hmacsha2.HmacSha2.sha384ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha384ServiceID),
|
||||
hmacsha2.HmacSha2.sha512ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha512ServiceID),
|
||||
noauth.NoAuth.serviceID: noauth.NoAuth()}
|
||||
|
||||
# Privacy services
|
||||
|
|
|
@ -13,7 +13,9 @@ __all__ = ['CommunityData', 'UsmUserData',
|
|||
'usmAesCfb192Protocol', 'usmAesCfb256Protocol',
|
||||
'usmAesBlumenthalCfb192Protocol', 'usmAesBlumenthalCfb256Protocol',
|
||||
'usmDESPrivProtocol', 'usmHMACMD5AuthProtocol',
|
||||
'usmHMACSHAAuthProtocol', 'usmNoAuthProtocol',
|
||||
'usmHMACSHAAuthProtocol', 'usmHMAC128SHA224AuthProtocol',
|
||||
'usmHMAC192SHA256AuthProtocol', 'usmHMAC256SHA384AuthProtocol',
|
||||
'usmHMAC384SHA512AuthProtocol', 'usmNoAuthProtocol',
|
||||
'usmNoPrivProtocol']
|
||||
|
||||
|
||||
|
@ -156,6 +158,11 @@ usmNoAuthProtocol = config.usmNoAuthProtocol
|
|||
usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol
|
||||
#: The HMAC-SHA-96 Digest Authentication Protocol (:RFC:`3414#section-7`)
|
||||
usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol
|
||||
#: The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)
|
||||
usmHMAC128SHA224AuthProtocol = config.usmHMAC128SHA224AuthProtocol
|
||||
usmHMAC192SHA256AuthProtocol = config.usmHMAC192SHA256AuthProtocol
|
||||
usmHMAC256SHA384AuthProtocol = config.usmHMAC256SHA384AuthProtocol
|
||||
usmHMAC384SHA512AuthProtocol = config.usmHMAC384SHA512AuthProtocol
|
||||
|
||||
#: No Privacy Protocol.
|
||||
usmNoPrivProtocol = config.usmNoPrivProtocol
|
||||
|
@ -214,6 +221,10 @@ class UsmUserData(object):
|
|||
* :py:class:`~pysnmp.hlapi.usmNoAuthProtocol` (default is *authKey* not given)
|
||||
* :py:class:`~pysnmp.hlapi.usmHMACMD5AuthProtocol` (default if *authKey* is given)
|
||||
* :py:class:`~pysnmp.hlapi.usmHMACSHAAuthProtocol`
|
||||
* :py:class:`~pysnmp.hlapi.usmHMAC128SHA224AuthProtocol`
|
||||
* :py:class:`~pysnmp.hlapi.usmHMAC192SHA256AuthProtocol`
|
||||
* :py:class:`~pysnmp.hlapi.usmHMAC256SHA384AuthProtocol`
|
||||
* :py:class:`~pysnmp.hlapi.usmHMAC384SHA512AuthProtocol`
|
||||
privProtocol: py:class:`tuple`
|
||||
An indication of whether messages sent on behalf of this USM user
|
||||
be encrypted, and if so, the type of encryption protocol which is used.
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#
|
||||
from pysnmp.proto.secmod.rfc3826.priv import aes
|
||||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto.secmod.rfc3414 import localkey
|
||||
from pysnmp.proto import error
|
||||
from math import ceil
|
||||
|
@ -64,18 +65,19 @@ class AbstractAesReeder(aes.Aes):
|
|||
# 2.1 of https://tools.itef.org/pdf/draft_bluementhal-aes-usm-04.txt
|
||||
def localizeKey(self, authProtocol, privKey, snmpEngineID):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
|
||||
# now extend this key if too short by repeating steps that includes the hashPassphrase step
|
||||
while len(localPrivKey) < self.keySize:
|
||||
newKey = localkey.hashPassphraseMD5(localPrivKey) # this is the difference between reeder and bluementhal
|
||||
localPrivKey += localkey.localizeKeyMD5(newKey, snmpEngineID)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
|
||||
while len(localPrivKey) < self.keySize:
|
||||
newKey = localkey.hashPassphraseSHA(localPrivKey)
|
||||
localPrivKey += localkey.localizeKeySHA(newKey, snmpEngineID)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
|
||||
# now extend this key if too short by repeating steps that includes the hashPassphrase step
|
||||
while len(localPrivKey) < self.keySize:
|
||||
# this is the difference between reeder and bluementhal
|
||||
newKey = localkey.hashPassphrase(localPrivKey, hashAlgo)
|
||||
localPrivKey += localkey.localizeKey(newKey, snmpEngineID, hashAlgo)
|
||||
return localPrivKey[:self.keySize]
|
||||
|
|
|
@ -8,6 +8,7 @@ import random
|
|||
from pysnmp.proto.secmod.rfc3414.priv import base
|
||||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
|
||||
from pysnmp.proto.secmod.rfc3414 import localkey
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto import errind, error
|
||||
from pyasn1.type import univ
|
||||
from pyasn1.compat.octets import null
|
||||
|
@ -43,31 +44,30 @@ class Des3(base.AbstractEncryptionService):
|
|||
|
||||
def hashPassphrase(self, authProtocol, privKey):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
return localkey.hashPassphraseMD5(privKey)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
return localkey.hashPassphraseSHA(privKey)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
return localkey.hashPassphrase(privKey, hashAlgo)
|
||||
|
||||
# 2.1
|
||||
def localizeKey(self, authProtocol, privKey, snmpEngineID):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
|
||||
# now extend this key if too short by repeating steps that includes the hashPassphrase step
|
||||
while len(localPrivKey) < self.keySize:
|
||||
newKey = localkey.hashPassphraseMD5(localPrivKey)
|
||||
localPrivKey += localkey.localizeKeyMD5(newKey, snmpEngineID)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
|
||||
while len(localPrivKey) < self.keySize:
|
||||
newKey = localkey.hashPassphraseSHA(localPrivKey)
|
||||
localPrivKey += localkey.localizeKeySHA(newKey, snmpEngineID)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
|
||||
return localPrivKey[:self.keySize]
|
||||
|
||||
# 5.1.1.1
|
||||
|
|
|
@ -15,6 +15,9 @@ class AbstractAuthenticationService(object):
|
|||
|
||||
def localizeKey(self, authKey, snmpEngineID):
|
||||
raise error.ProtocolError(errind.noAuthentication)
|
||||
|
||||
def getTagLen(self):
|
||||
raise error.ProtocolError(errind.noAuthentication)
|
||||
|
||||
# 7.2.4.1
|
||||
def authenticateOutgoingMsg(self, authKey, wholeMsg):
|
||||
|
|
|
@ -32,6 +32,9 @@ class HmacMd5(base.AbstractAuthenticationService):
|
|||
def localizeKey(self, authKey, snmpEngineID):
|
||||
return localkey.localizeKeyMD5(authKey, snmpEngineID)
|
||||
|
||||
def getTagLen(self):
|
||||
return 12
|
||||
|
||||
# 6.3.1
|
||||
def authenticateOutgoingMsg(self, authKey, wholeMsg):
|
||||
# Here we expect calling secmod to indicate where the digest
|
||||
|
|
|
@ -32,6 +32,9 @@ class HmacSha(base.AbstractAuthenticationService):
|
|||
def localizeKey(self, authKey, snmpEngineID):
|
||||
return localkey.localizeKeySHA(authKey, snmpEngineID)
|
||||
|
||||
def getTagLen(self):
|
||||
return 12
|
||||
|
||||
# 7.3.1
|
||||
def authenticateOutgoingMsg(self, authKey, wholeMsg):
|
||||
# 7.3.1.1
|
||||
|
|
|
@ -15,44 +15,10 @@ except ImportError:
|
|||
from pyasn1.type import univ
|
||||
|
||||
|
||||
# RFC3414: A.2.1
|
||||
def hashPassphraseMD5(passphrase):
|
||||
def hashPassphrase(passphrase, hashFunc):
|
||||
passphrase = univ.OctetString(passphrase).asOctets()
|
||||
# noinspection PyDeprecation,PyCallingNonCallable
|
||||
md = md5()
|
||||
ringBuffer = passphrase * (passphrase and (64 // len(passphrase) + 1) or 1)
|
||||
# noinspection PyTypeChecker
|
||||
ringBufferLen = len(ringBuffer)
|
||||
count = 0
|
||||
mark = 0
|
||||
while count < 16384:
|
||||
e = mark + 64
|
||||
if e < ringBufferLen:
|
||||
md.update(ringBuffer[mark:e])
|
||||
mark = e
|
||||
else:
|
||||
md.update(
|
||||
ringBuffer[mark:ringBufferLen] + ringBuffer[0:e - ringBufferLen]
|
||||
)
|
||||
mark = e - ringBufferLen
|
||||
count += 1
|
||||
return md.digest()
|
||||
|
||||
|
||||
def localizeKeyMD5(passKey, snmpEngineId):
|
||||
passKey = univ.OctetString(passKey).asOctets()
|
||||
# noinspection PyDeprecation,PyCallingNonCallable
|
||||
return md5(passKey + snmpEngineId.asOctets() + passKey).digest()
|
||||
|
||||
|
||||
def passwordToKeyMD5(passphrase, snmpEngineId):
|
||||
return localizeKeyMD5(hashPassphraseMD5(passphrase), snmpEngineId)
|
||||
|
||||
|
||||
# RFC3414: A.2.2
|
||||
def hashPassphraseSHA(passphrase):
|
||||
passphrase = univ.OctetString(passphrase).asOctets()
|
||||
md = sha1()
|
||||
hasher = hashFunc()
|
||||
ringBuffer = passphrase * (64 // len(passphrase) + 1)
|
||||
# noinspection PyTypeChecker
|
||||
ringBufferLen = len(ringBuffer)
|
||||
|
@ -61,21 +27,40 @@ def hashPassphraseSHA(passphrase):
|
|||
while count < 16384:
|
||||
e = mark + 64
|
||||
if e < ringBufferLen:
|
||||
md.update(ringBuffer[mark:e])
|
||||
hasher.update(ringBuffer[mark:e])
|
||||
mark = e
|
||||
else:
|
||||
md.update(
|
||||
hasher.update(
|
||||
ringBuffer[mark:ringBufferLen] + ringBuffer[0:e - ringBufferLen]
|
||||
)
|
||||
mark = e - ringBufferLen
|
||||
count += 1
|
||||
return md.digest()
|
||||
return hasher.digest()
|
||||
|
||||
def passwordToKey(passphrase, snmpEngineId, hashFunc):
|
||||
return localizeKey(hashPassphrase(passphrase, hashFunc), snmpEngineId, hashFunc)
|
||||
|
||||
def localizeKeySHA(passKey, snmpEngineId):
|
||||
def localizeKey(passKey, snmpEngineId, hashFunc):
|
||||
passKey = univ.OctetString(passKey).asOctets()
|
||||
return sha1(passKey + snmpEngineId.asOctets() + passKey).digest()
|
||||
# noinspection PyDeprecation,PyCallingNonCallable
|
||||
return hashFunc(passKey + snmpEngineId.asOctets() + passKey).digest()
|
||||
|
||||
# RFC3414: A.2.1
|
||||
def hashPassphraseMD5(passphrase):
|
||||
return hashPassphrase(passphrase, md5)
|
||||
|
||||
# RFC3414: A.2.2
|
||||
def hashPassphraseSHA(passphrase):
|
||||
return hashPassphrase(passphrase, sha1)
|
||||
|
||||
def passwordToKeyMD5(passphrase, snmpEngineId):
|
||||
return localizeKey(hashPassphraseMD5(passphrase), snmpEngineId, md5)
|
||||
|
||||
def passwordToKeySHA(passphrase, snmpEngineId):
|
||||
return localizeKeySHA(hashPassphraseSHA(passphrase), snmpEngineId)
|
||||
return localizeKey(hashPassphraseMD5(passphrase), snmpEngineId, sha1)
|
||||
|
||||
def localizeKeyMD5(passKey, snmpEngineId):
|
||||
return localizeKey(passKey, snmpEngineId, md5)
|
||||
|
||||
def localizeKeySHA(passKey, snmpEngineId):
|
||||
return localizeKey(passKey, snmpEngineId, sha1)
|
||||
|
|
|
@ -8,6 +8,7 @@ import random
|
|||
from pysnmp.proto.secmod.rfc3414.priv import base
|
||||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
|
||||
from pysnmp.proto.secmod.rfc3414 import localkey
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto import errind, error
|
||||
from pyasn1.type import univ
|
||||
from sys import version_info
|
||||
|
@ -16,6 +17,14 @@ try:
|
|||
from Crypto.Cipher import DES
|
||||
except ImportError:
|
||||
DES = None
|
||||
try:
|
||||
from hashlib import md5, sha1
|
||||
except ImportError:
|
||||
import md5
|
||||
import sha
|
||||
|
||||
md5 = md5.new
|
||||
sha1 = sha.new
|
||||
|
||||
random.seed()
|
||||
|
||||
|
@ -33,23 +42,29 @@ class Des(base.AbstractEncryptionService):
|
|||
|
||||
def hashPassphrase(self, authProtocol, privKey):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
return localkey.hashPassphraseMD5(privKey)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
return localkey.hashPassphraseSHA(privKey)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
return localkey.hashPassphrase(privKey, hashAlgo)
|
||||
|
||||
def localizeKey(self, authProtocol, privKey, snmpEngineID):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
|
||||
return localPrivKey[:self.keySize]
|
||||
|
||||
# 8.1.1.1
|
||||
|
|
|
@ -10,6 +10,7 @@ from pysnmp.proto.secmod.base import AbstractSecurityModel
|
|||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha, noauth
|
||||
from pysnmp.proto.secmod.rfc3414.priv import des, nopriv
|
||||
from pysnmp.proto.secmod.rfc3826.priv import aes
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256
|
||||
from pysnmp.smi.error import NoSuchInstanceError
|
||||
from pysnmp.proto import rfc1155, errind, error
|
||||
|
@ -40,7 +41,12 @@ class SnmpUSMSecurityModel(AbstractSecurityModel):
|
|||
securityModelID = 3
|
||||
authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
|
||||
hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
|
||||
noauth.NoAuth.serviceID: noauth.NoAuth()}
|
||||
hmacsha2.HmacSha2.sha224ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha224ServiceID),
|
||||
hmacsha2.HmacSha2.sha256ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha256ServiceID),
|
||||
hmacsha2.HmacSha2.sha384ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha384ServiceID),
|
||||
hmacsha2.HmacSha2.sha512ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha512ServiceID),
|
||||
noauth.NoAuth.serviceID: noauth.NoAuth(),
|
||||
}
|
||||
privServices = {des.Des.serviceID: des.Des(),
|
||||
des3.Des3.serviceID: des3.Des3(),
|
||||
aes.Aes.serviceID: aes.Aes(),
|
||||
|
@ -432,7 +438,7 @@ class SnmpUSMSecurityModel(AbstractSecurityModel):
|
|||
|
||||
# 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'
|
||||
4, '\x00' * authHandler.getTagLen()
|
||||
)
|
||||
|
||||
debug.logger & debug.flagSM and debug.logger(
|
||||
|
|
|
@ -8,6 +8,7 @@ import random
|
|||
from pyasn1.type import univ
|
||||
from pysnmp.proto.secmod.rfc3414.priv import base
|
||||
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
|
||||
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
|
||||
from pysnmp.proto.secmod.rfc3414 import localkey
|
||||
from pysnmp.proto import errind, error
|
||||
|
||||
|
@ -15,6 +16,14 @@ try:
|
|||
from Crypto.Cipher import AES
|
||||
except ImportError:
|
||||
AES = None
|
||||
try:
|
||||
from hashlib import md5, sha1
|
||||
except ImportError:
|
||||
import md5
|
||||
import sha
|
||||
|
||||
md5 = md5.new
|
||||
sha1 = sha.new
|
||||
|
||||
random.seed()
|
||||
|
||||
|
@ -66,23 +75,29 @@ class Aes(base.AbstractEncryptionService):
|
|||
|
||||
def hashPassphrase(self, authProtocol, privKey):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
return localkey.hashPassphraseMD5(privKey)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
return localkey.hashPassphraseSHA(privKey)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
return localkey.hashPassphrase(privKey, hashAlgo)
|
||||
|
||||
def localizeKey(self, authProtocol, privKey, snmpEngineID):
|
||||
if authProtocol == hmacmd5.HmacMd5.serviceID:
|
||||
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
|
||||
hashAlgo = md5
|
||||
elif authProtocol == hmacsha.HmacSha.serviceID:
|
||||
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
|
||||
hashAlgo = sha1
|
||||
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
|
||||
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
|
||||
else:
|
||||
raise error.ProtocolError(
|
||||
'Unknown auth protocol %s' % (authProtocol,)
|
||||
)
|
||||
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
|
||||
return localPrivKey[:self.keySize]
|
||||
|
||||
# 3.2.4.1
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
# This file is necessary to make this directory a package.
|
|
@ -0,0 +1 @@
|
|||
# This file is necessary to make this directory a package.
|
|
@ -0,0 +1,110 @@
|
|||
#
|
||||
# This file is part of pysnmp software.
|
||||
#
|
||||
# Copyright (c) 2005-2017, Olivier Verriest <verri@x25.pm>
|
||||
# License: http://pysnmp.sf.net/license.html
|
||||
#
|
||||
try:
|
||||
from hashlib import sha224, sha256, sha384, sha512
|
||||
import hmac
|
||||
except ImportError:
|
||||
import logging
|
||||
logging.debug('SHA-2 HMAC authentication unavailable', exc_info=True)
|
||||
|
||||
from pyasn1.type import univ
|
||||
from pysnmp.proto.secmod.rfc3414.auth import base
|
||||
from pysnmp.proto.secmod.rfc3414 import localkey
|
||||
from pysnmp.proto import errind, error
|
||||
|
||||
# 7.2.4
|
||||
|
||||
class HmacSha2(base.AbstractAuthenticationService):
|
||||
sha224ServiceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 4) # usmHMAC128SHA224AuthProtocol
|
||||
sha256ServiceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 5) # usmHMAC192SHA256AuthProtocol
|
||||
sha384ServiceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 6) # usmHMAC256SHA384AuthProtocol
|
||||
sha512ServiceID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 7) # usmHMAC384SHA512AuthProtocol
|
||||
keyLength = {
|
||||
sha224ServiceID : 28,
|
||||
sha256ServiceID : 32,
|
||||
sha384ServiceID : 48,
|
||||
sha512ServiceID : 64
|
||||
}
|
||||
tagLength = {
|
||||
sha224ServiceID : 16,
|
||||
sha256ServiceID : 24,
|
||||
sha384ServiceID : 32,
|
||||
sha512ServiceID : 48
|
||||
}
|
||||
hashAlgo = {
|
||||
sha224ServiceID : sha224,
|
||||
sha256ServiceID : sha256,
|
||||
sha384ServiceID : sha384,
|
||||
sha512ServiceID : sha512
|
||||
}
|
||||
|
||||
__ipad = [0x36] * 64
|
||||
__opad = [0x5C] * 64
|
||||
|
||||
def __init__(self, oid):
|
||||
if not oid in HmacSha2.hashAlgo:
|
||||
raise error.ProtocolError('no such SHA-2 authentication algorithm', oid)
|
||||
self.__hashAlgo = HmacSha2.hashAlgo[oid]
|
||||
self.__tagLength = HmacSha2.tagLength[oid]
|
||||
self.__placeHolder = univ.OctetString((0,) * self.__tagLength).asOctets()
|
||||
|
||||
def hashPassphrase(self, authKey):
|
||||
return localkey.hashPassphrase(authKey, self.__hashAlgo)
|
||||
|
||||
def localizeKey(self, authKey, snmpEngineID):
|
||||
return localkey.localizeKey(authKey, snmpEngineID, self.__hashAlgo)
|
||||
|
||||
def getTagLen(self):
|
||||
return self.__tagLength
|
||||
|
||||
# 7.3.1
|
||||
def authenticateOutgoingMsg(self, authKey, wholeMsg):
|
||||
# 7.3.1.1
|
||||
l = wholeMsg.find(self.__placeHolder)
|
||||
if l == -1:
|
||||
raise error.ProtocolError('Can\'t locate digest placeholder')
|
||||
wholeHead = wholeMsg[:l]
|
||||
wholeTail = wholeMsg[l + self.__tagLength:]
|
||||
|
||||
# 7.3.1.2, 7.3.1.3
|
||||
mac = hmac.new(authKey.asOctets(), wholeMsg, self.__hashAlgo)
|
||||
|
||||
# 7.3.1.4
|
||||
mac = mac.digest()[:self.__tagLength]
|
||||
|
||||
# 7.3.1.5 & 6
|
||||
return wholeHead + mac + wholeTail
|
||||
|
||||
# 7.3.2
|
||||
def authenticateIncomingMsg(self, authKey, authParameters, wholeMsg):
|
||||
# 7.3.2.1 & 2
|
||||
if len(authParameters) != self.__tagLength:
|
||||
raise error.StatusInformation(
|
||||
errorIndication=errind.authenticationError
|
||||
)
|
||||
|
||||
# 7.3.2.3
|
||||
l = wholeMsg.find(authParameters.asOctets())
|
||||
if l == -1:
|
||||
raise error.ProtocolError('Can\'t locate digest in wholeMsg')
|
||||
wholeHead = wholeMsg[:l]
|
||||
wholeTail = wholeMsg[l + self.__tagLength:]
|
||||
authenticatedWholeMsg = wholeHead + self.__placeHolder + wholeTail
|
||||
|
||||
# 7.3.2.4
|
||||
mac = hmac.new(authKey.asOctets(), authenticatedWholeMsg, self.__hashAlgo)
|
||||
|
||||
# 7.3.2.5
|
||||
mac = mac.digest()[:self.__tagLength]
|
||||
|
||||
# 7.3.2.6
|
||||
if mac != authParameters:
|
||||
raise error.StatusInformation(
|
||||
errorIndication=errind.authenticationFailure
|
||||
)
|
||||
|
||||
return authenticatedWholeMsg
|
|
@ -61,16 +61,16 @@ usmUserEntry.registerAugmentions(("PYSNMP-USM-MIB", "pysnmpUsmKeyEntry"))
|
|||
pysnmpUsmKeyEntry.setIndexNames(*usmUserEntry.getIndexNames())
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyEntry.setStatus('current')
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyEntry.setDescription('Information about a particular USM user credentials.')
|
||||
pysnmpUsmKeyAuthLocalized = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 1), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 32)))
|
||||
pysnmpUsmKeyAuthLocalized = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 1), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 64)))
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyAuthLocalized.setStatus('current')
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyAuthLocalized.setDescription("User's localized key used for authentication.")
|
||||
pysnmpUsmKeyPrivLocalized = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 2), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 32)))
|
||||
pysnmpUsmKeyPrivLocalized = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 2), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 64)))
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyPrivLocalized.setStatus('current')
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyPrivLocalized.setDescription("User's localized key used for encryption.")
|
||||
pysnmpUsmKeyAuth = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 3), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 32)))
|
||||
pysnmpUsmKeyAuth = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 3), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 64)))
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyAuth.setStatus('current')
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyAuth.setDescription("User's non-localized key used for authentication.")
|
||||
pysnmpUsmKeyPriv = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 4), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 32)))
|
||||
pysnmpUsmKeyPriv = MibTableColumn((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 1, 3, 1, 4), OctetString('\x00\x00\x00\x00\x00\x00\x00\x00').subtype(subtypeSpec=ValueSizeConstraint(8, 64)))
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyPriv.setStatus('current')
|
||||
if mibBuilder.loadTexts: pysnmpUsmKeyPriv.setDescription("User's non-localized key used for encryption.")
|
||||
pysnmpUsmMIBCompliances = MibIdentifier((1, 3, 6, 1, 4, 1, 20408, 3, 1, 1, 2, 1))
|
||||
|
|
2
setup.py
2
setup.py
|
@ -118,6 +118,8 @@ params.update({
|
|||
'pysnmp.proto.secmod.rfc3414.priv',
|
||||
'pysnmp.proto.secmod.rfc3826',
|
||||
'pysnmp.proto.secmod.rfc3826.priv',
|
||||
'pysnmp.proto.secmod.rfc7860',
|
||||
'pysnmp.proto.secmod.rfc7860.auth',
|
||||
'pysnmp.proto.secmod.eso',
|
||||
'pysnmp.proto.secmod.eso.priv',
|
||||
'pysnmp.proto.acmod',
|
||||
|
|
Loading…
Reference in New Issue