add support for USM SHA-2 algorithms (RFC 7860) (#71)

pull/82/head
verrio 2017-08-02 23:38:14 +00:00 committed by Ilya Etingof
parent 801d47b632
commit 130bdaa6e2
20 changed files with 258 additions and 82 deletions

4
.gitignore vendored
View File

@ -13,3 +13,7 @@ docs/source/.templates/
# PyCharm stuff
.idea/
# Eclipse stuff
.project
.pydevproject

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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]

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -0,0 +1 @@
# This file is necessary to make this directory a package.

View File

@ -0,0 +1 @@
# This file is necessary to make this directory a package.

View File

@ -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

View File

@ -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))

View File

@ -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',