summaryrefslogtreecommitdiff
path: root/pysnmp
diff options
context:
space:
mode:
authorverrio <verri@x25.pm>2017-08-02 23:38:14 +0000
committerIlya Etingof <etingof@gmail.com>2017-08-03 01:38:14 +0200
commit130bdaa6e218c95806bd571b326a5d3e2ddcb1ef (patch)
treea379ae562ecd5ee4b9cc998ae666f9f8d247db73 /pysnmp
parent801d47b63222f46dd403e145f9dcff6a03dd3337 (diff)
downloadpysnmp-git-130bdaa6e218c95806bd571b326a5d3e2ddcb1ef.tar.gz
add support for USM SHA-2 algorithms (RFC 7860) (#71)
Diffstat (limited to 'pysnmp')
-rw-r--r--pysnmp/entity/config.py9
-rw-r--r--pysnmp/hlapi/auth.py13
-rw-r--r--pysnmp/proto/secmod/eso/priv/aesbase.py20
-rw-r--r--pysnmp/proto/secmod/eso/priv/des3.py22
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/base.py3
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py3
-rw-r--r--pysnmp/proto/secmod/rfc3414/auth/hmacsha.py3
-rw-r--r--pysnmp/proto/secmod/rfc3414/localkey.py59
-rw-r--r--pysnmp/proto/secmod/rfc3414/priv/des.py23
-rw-r--r--pysnmp/proto/secmod/rfc3414/service.py10
-rw-r--r--pysnmp/proto/secmod/rfc3826/priv/aes.py23
-rw-r--r--pysnmp/proto/secmod/rfc7860/__init__.py1
-rw-r--r--pysnmp/proto/secmod/rfc7860/auth/__init__.py1
-rw-r--r--pysnmp/proto/secmod/rfc7860/auth/hmacsha2.py110
-rw-r--r--pysnmp/smi/mibs/PYSNMP-USM-MIB.py8
15 files changed, 236 insertions, 72 deletions
diff --git a/pysnmp/entity/config.py b/pysnmp/entity/config.py
index e66b7e58..0176edc5 100644
--- a/pysnmp/entity/config.py
+++ b/pysnmp/entity/config.py
@@ -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
diff --git a/pysnmp/hlapi/auth.py b/pysnmp/hlapi/auth.py
index 0a871431..20541f7c 100644
--- a/pysnmp/hlapi/auth.py
+++ b/pysnmp/hlapi/auth.py
@@ -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.
diff --git a/pysnmp/proto/secmod/eso/priv/aesbase.py b/pysnmp/proto/secmod/eso/priv/aesbase.py
index 3f8408c2..43bb724b 100644
--- a/pysnmp/proto/secmod/eso/priv/aesbase.py
+++ b/pysnmp/proto/secmod/eso/priv/aesbase.py
@@ -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]
diff --git a/pysnmp/proto/secmod/eso/priv/des3.py b/pysnmp/proto/secmod/eso/priv/des3.py
index 07e48151..16d770e9 100644
--- a/pysnmp/proto/secmod/eso/priv/des3.py
+++ b/pysnmp/proto/secmod/eso/priv/des3.py
@@ -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
diff --git a/pysnmp/proto/secmod/rfc3414/auth/base.py b/pysnmp/proto/secmod/rfc3414/auth/base.py
index e412adea..133416b8 100644
--- a/pysnmp/proto/secmod/rfc3414/auth/base.py
+++ b/pysnmp/proto/secmod/rfc3414/auth/base.py
@@ -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):
diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py
index 35a95b32..13b27e10 100644
--- a/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py
+++ b/pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py
@@ -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
diff --git a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py
index 5bc3c457..16cb2149 100644
--- a/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py
+++ b/pysnmp/proto/secmod/rfc3414/auth/hmacsha.py
@@ -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
diff --git a/pysnmp/proto/secmod/rfc3414/localkey.py b/pysnmp/proto/secmod/rfc3414/localkey.py
index beb807a0..c5105821 100644
--- a/pysnmp/proto/secmod/rfc3414/localkey.py
+++ b/pysnmp/proto/secmod/rfc3414/localkey.py
@@ -15,12 +15,11 @@ 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)
+ hasher = hashFunc()
+ ringBuffer = passphrase * (64 // len(passphrase) + 1)
# noinspection PyTypeChecker
ringBufferLen = len(ringBuffer)
count = 0
@@ -28,54 +27,40 @@ def hashPassphraseMD5(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 localizeKeyMD5(passKey, snmpEngineId):
+def localizeKey(passKey, snmpEngineId, hashFunc):
passKey = univ.OctetString(passKey).asOctets()
# noinspection PyDeprecation,PyCallingNonCallable
- return md5(passKey + snmpEngineId.asOctets() + passKey).digest()
-
-
-def passwordToKeyMD5(passphrase, snmpEngineId):
- return localizeKeyMD5(hashPassphraseMD5(passphrase), snmpEngineId)
+ 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):
- passphrase = univ.OctetString(passphrase).asOctets()
- md = sha1()
- ringBuffer = passphrase * (64 // len(passphrase) + 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()
+ return hashPassphrase(passphrase, sha1)
+def passwordToKeyMD5(passphrase, snmpEngineId):
+ return localizeKey(hashPassphraseMD5(passphrase), snmpEngineId, md5)
-def localizeKeySHA(passKey, snmpEngineId):
- passKey = univ.OctetString(passKey).asOctets()
- return sha1(passKey + snmpEngineId.asOctets() + passKey).digest()
+def passwordToKeySHA(passphrase, snmpEngineId):
+ return localizeKey(hashPassphraseMD5(passphrase), snmpEngineId, sha1)
+def localizeKeyMD5(passKey, snmpEngineId):
+ return localizeKey(passKey, snmpEngineId, md5)
-def passwordToKeySHA(passphrase, snmpEngineId):
- return localizeKeySHA(hashPassphraseSHA(passphrase), snmpEngineId)
+def localizeKeySHA(passKey, snmpEngineId):
+ return localizeKey(passKey, snmpEngineId, sha1)
diff --git a/pysnmp/proto/secmod/rfc3414/priv/des.py b/pysnmp/proto/secmod/rfc3414/priv/des.py
index 57f47c33..e38239ad 100644
--- a/pysnmp/proto/secmod/rfc3414/priv/des.py
+++ b/pysnmp/proto/secmod/rfc3414/priv/des.py
@@ -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
diff --git a/pysnmp/proto/secmod/rfc3414/service.py b/pysnmp/proto/secmod/rfc3414/service.py
index 4d7500b4..d0a88c9c 100644
--- a/pysnmp/proto/secmod/rfc3414/service.py
+++ b/pysnmp/proto/secmod/rfc3414/service.py
@@ -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(
diff --git a/pysnmp/proto/secmod/rfc3826/priv/aes.py b/pysnmp/proto/secmod/rfc3826/priv/aes.py
index 06c8eb3f..734e9643 100644
--- a/pysnmp/proto/secmod/rfc3826/priv/aes.py
+++ b/pysnmp/proto/secmod/rfc3826/priv/aes.py
@@ -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
diff --git a/pysnmp/proto/secmod/rfc7860/__init__.py b/pysnmp/proto/secmod/rfc7860/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc7860/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pysnmp/proto/secmod/rfc7860/auth/__init__.py b/pysnmp/proto/secmod/rfc7860/auth/__init__.py
new file mode 100644
index 00000000..8c3066b2
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc7860/auth/__init__.py
@@ -0,0 +1 @@
+# This file is necessary to make this directory a package.
diff --git a/pysnmp/proto/secmod/rfc7860/auth/hmacsha2.py b/pysnmp/proto/secmod/rfc7860/auth/hmacsha2.py
new file mode 100644
index 00000000..60453c53
--- /dev/null
+++ b/pysnmp/proto/secmod/rfc7860/auth/hmacsha2.py
@@ -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
diff --git a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py
index f7d2c519..19351159 100644
--- a/pysnmp/smi/mibs/PYSNMP-USM-MIB.py
+++ b/pysnmp/smi/mibs/PYSNMP-USM-MIB.py
@@ -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))