Skip to content

Commit 130bdaa

Browse files
verrioetingof
authored andcommitted
add support for USM SHA-2 algorithms (RFC 7860) (#71)
1 parent 801d47b commit 130bdaa

20 files changed

Lines changed: 253 additions & 77 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,7 @@ docs/source/.templates/
1313

1414
# PyCharm stuff
1515
.idea/
16+
17+
# Eclipse stuff
18+
.project
19+
.pydevproject

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Features
3232

3333
Features, specific to SNMPv3 model include:
3434

35-
* USM authentication (MD5/SHA) and privacy (DES/AES) protocols (RFC3414)
35+
* USM authentication (MD5/SHA-1/SHA-2) and privacy (DES/AES) protocols (RFC3414, RFC7860)
3636
* View-based access control to use with any SNMP model (RFC3415)
3737
* Built-in SNMP proxy PDU converter for building multi-lingual
3838
SNMP entities (RFC2576)

docs/mibs/PYSNMP-USM-MIB.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ pysnmpUsmMIB MODULE-IDENTITY
2121
DESCRIPTION
2222
"This MIB module defines objects specific to User
2323
Security Model (USM) implementation at PySNMP."
24+
REVISION "201707300000Z"
25+
DESCRIPTION "Extended authentication key size"
2426
REVISION "201704140000Z"
2527
DESCRIPTION "Updated addresses"
2628
REVISION "200505140000Z" -- 14 May 2005, midnight
@@ -153,31 +155,31 @@ PysnmpUsmKeyEntry ::= SEQUENCE {
153155
}
154156

155157
pysnmpUsmKeyAuthLocalized OBJECT-TYPE
156-
SYNTAX OCTET STRING (SIZE(8..32))
158+
SYNTAX OCTET STRING (SIZE(8..64))
157159
MAX-ACCESS not-accessible
158160
STATUS current
159161
DESCRIPTION
160162
"User's localized key used for authentication."
161163
::= { pysnmpUsmKeyEntry 1 }
162164

163165
pysnmpUsmKeyPrivLocalized OBJECT-TYPE
164-
SYNTAX OCTET STRING (SIZE(8..32))
166+
SYNTAX OCTET STRING (SIZE(8..64))
165167
MAX-ACCESS not-accessible
166168
STATUS current
167169
DESCRIPTION
168170
"User's localized key used for encryption."
169171
::= { pysnmpUsmKeyEntry 2 }
170172

171173
pysnmpUsmKeyAuth OBJECT-TYPE
172-
SYNTAX OCTET STRING (SIZE(8..32))
174+
SYNTAX OCTET STRING (SIZE(8..64))
173175
MAX-ACCESS not-accessible
174176
STATUS current
175177
DESCRIPTION
176178
"User's non-localized key used for authentication."
177179
::= { pysnmpUsmKeyEntry 3 }
178180

179181
pysnmpUsmKeyPriv OBJECT-TYPE
180-
SYNTAX OCTET STRING (SIZE(8..32))
182+
SYNTAX OCTET STRING (SIZE(8..64))
181183
MAX-ACCESS not-accessible
182184
STATUS current
183185
DESCRIPTION

docs/source/docs/api-reference.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ via constant OIDs:
225225
.. autodata:: pysnmp.hlapi.usmNoAuthProtocol
226226
.. autodata:: pysnmp.hlapi.usmHMACMD5AuthProtocol
227227
.. autodata:: pysnmp.hlapi.usmHMACSHAAuthProtocol
228+
.. autodata:: pysnmp.hlapi.usmHMAC128SHA224AuthProtocol
229+
.. autodata:: pysnmp.hlapi.usmHMAC192SHA256AuthProtocol
230+
.. autodata:: pysnmp.hlapi.usmHMAC256SHA384AuthProtocol
231+
.. autodata:: pysnmp.hlapi.usmHMAC384SHA512AuthProtocol
228232

229233
.. autodata:: pysnmp.hlapi.usmNoPrivProtocol
230234
.. autodata:: pysnmp.hlapi.usmDESPrivProtocol

pysnmp/entity/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha, noauth
1010
from pysnmp.proto.secmod.rfc3414.priv import des, nopriv
1111
from pysnmp.proto.secmod.rfc3826.priv import aes
12+
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
1213
from pysnmp.proto.secmod.eso.priv import des3, aes192, aes256
1314
from pysnmp.proto import rfc1905
1415
from pysnmp import error
@@ -23,6 +24,10 @@
2324
# Auth protocol
2425
usmHMACMD5AuthProtocol = hmacmd5.HmacMd5.serviceID
2526
usmHMACSHAAuthProtocol = hmacsha.HmacSha.serviceID
27+
usmHMAC128SHA224AuthProtocol = hmacsha2.HmacSha2.sha224ServiceID
28+
usmHMAC192SHA256AuthProtocol = hmacsha2.HmacSha2.sha256ServiceID
29+
usmHMAC256SHA384AuthProtocol = hmacsha2.HmacSha2.sha384ServiceID
30+
usmHMAC384SHA512AuthProtocol = hmacsha2.HmacSha2.sha512ServiceID
2631
usmNoAuthProtocol = noauth.NoAuth.serviceID
2732

2833
# Privacy protocol
@@ -38,6 +43,10 @@
3843
# Auth services
3944
authServices = {hmacmd5.HmacMd5.serviceID: hmacmd5.HmacMd5(),
4045
hmacsha.HmacSha.serviceID: hmacsha.HmacSha(),
46+
hmacsha2.HmacSha2.sha224ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha224ServiceID),
47+
hmacsha2.HmacSha2.sha256ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha256ServiceID),
48+
hmacsha2.HmacSha2.sha384ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha384ServiceID),
49+
hmacsha2.HmacSha2.sha512ServiceID: hmacsha2.HmacSha2(hmacsha2.HmacSha2.sha512ServiceID),
4150
noauth.NoAuth.serviceID: noauth.NoAuth()}
4251

4352
# Privacy services

pysnmp/hlapi/auth.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
'usmAesCfb192Protocol', 'usmAesCfb256Protocol',
1414
'usmAesBlumenthalCfb192Protocol', 'usmAesBlumenthalCfb256Protocol',
1515
'usmDESPrivProtocol', 'usmHMACMD5AuthProtocol',
16-
'usmHMACSHAAuthProtocol', 'usmNoAuthProtocol',
16+
'usmHMACSHAAuthProtocol', 'usmHMAC128SHA224AuthProtocol',
17+
'usmHMAC192SHA256AuthProtocol', 'usmHMAC256SHA384AuthProtocol',
18+
'usmHMAC384SHA512AuthProtocol', 'usmNoAuthProtocol',
1719
'usmNoPrivProtocol']
1820

1921

@@ -156,6 +158,11 @@ def clone(self, communityIndex=None, communityName=None,
156158
usmHMACMD5AuthProtocol = config.usmHMACMD5AuthProtocol
157159
#: The HMAC-SHA-96 Digest Authentication Protocol (:RFC:`3414#section-7`)
158160
usmHMACSHAAuthProtocol = config.usmHMACSHAAuthProtocol
161+
#: The HMAC-SHA-2 Digest Authentication Protocols (:RFC:`7860`)
162+
usmHMAC128SHA224AuthProtocol = config.usmHMAC128SHA224AuthProtocol
163+
usmHMAC192SHA256AuthProtocol = config.usmHMAC192SHA256AuthProtocol
164+
usmHMAC256SHA384AuthProtocol = config.usmHMAC256SHA384AuthProtocol
165+
usmHMAC384SHA512AuthProtocol = config.usmHMAC384SHA512AuthProtocol
159166

160167
#: No Privacy Protocol.
161168
usmNoPrivProtocol = config.usmNoPrivProtocol
@@ -214,6 +221,10 @@ class instance.
214221
* :py:class:`~pysnmp.hlapi.usmNoAuthProtocol` (default is *authKey* not given)
215222
* :py:class:`~pysnmp.hlapi.usmHMACMD5AuthProtocol` (default if *authKey* is given)
216223
* :py:class:`~pysnmp.hlapi.usmHMACSHAAuthProtocol`
224+
* :py:class:`~pysnmp.hlapi.usmHMAC128SHA224AuthProtocol`
225+
* :py:class:`~pysnmp.hlapi.usmHMAC192SHA256AuthProtocol`
226+
* :py:class:`~pysnmp.hlapi.usmHMAC256SHA384AuthProtocol`
227+
* :py:class:`~pysnmp.hlapi.usmHMAC384SHA512AuthProtocol`
217228
privProtocol: py:class:`tuple`
218229
An indication of whether messages sent on behalf of this USM user
219230
be encrypted, and if so, the type of encryption protocol which is used.

pysnmp/proto/secmod/eso/priv/aesbase.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77
from pysnmp.proto.secmod.rfc3826.priv import aes
88
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
9+
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
910
from pysnmp.proto.secmod.rfc3414 import localkey
1011
from pysnmp.proto import error
1112
from math import ceil
@@ -64,18 +65,19 @@ class AbstractAesReeder(aes.Aes):
6465
# 2.1 of https://tools.itef.org/pdf/draft_bluementhal-aes-usm-04.txt
6566
def localizeKey(self, authProtocol, privKey, snmpEngineID):
6667
if authProtocol == hmacmd5.HmacMd5.serviceID:
67-
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
68-
# now extend this key if too short by repeating steps that includes the hashPassphrase step
69-
while len(localPrivKey) < self.keySize:
70-
newKey = localkey.hashPassphraseMD5(localPrivKey) # this is the difference between reeder and bluementhal
71-
localPrivKey += localkey.localizeKeyMD5(newKey, snmpEngineID)
68+
hashAlgo = md5
7269
elif authProtocol == hmacsha.HmacSha.serviceID:
73-
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
74-
while len(localPrivKey) < self.keySize:
75-
newKey = localkey.hashPassphraseSHA(localPrivKey)
76-
localPrivKey += localkey.localizeKeySHA(newKey, snmpEngineID)
70+
hashAlgo = sha1
71+
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
72+
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
7773
else:
7874
raise error.ProtocolError(
7975
'Unknown auth protocol %s' % (authProtocol,)
8076
)
77+
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
78+
# now extend this key if too short by repeating steps that includes the hashPassphrase step
79+
while len(localPrivKey) < self.keySize:
80+
# this is the difference between reeder and bluementhal
81+
newKey = localkey.hashPassphrase(localPrivKey, hashAlgo)
82+
localPrivKey += localkey.localizeKey(newKey, snmpEngineID, hashAlgo)
8183
return localPrivKey[:self.keySize]

pysnmp/proto/secmod/eso/priv/des3.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from pysnmp.proto.secmod.rfc3414.priv import base
99
from pysnmp.proto.secmod.rfc3414.auth import hmacmd5, hmacsha
1010
from pysnmp.proto.secmod.rfc3414 import localkey
11+
from pysnmp.proto.secmod.rfc7860.auth import hmacsha2
1112
from pysnmp.proto import errind, error
1213
from pyasn1.type import univ
1314
from pyasn1.compat.octets import null
@@ -43,31 +44,30 @@ class Des3(base.AbstractEncryptionService):
4344

4445
def hashPassphrase(self, authProtocol, privKey):
4546
if authProtocol == hmacmd5.HmacMd5.serviceID:
46-
return localkey.hashPassphraseMD5(privKey)
47+
hashAlgo = md5
4748
elif authProtocol == hmacsha.HmacSha.serviceID:
48-
return localkey.hashPassphraseSHA(privKey)
49+
hashAlgo = sha1
50+
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
51+
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
4952
else:
5053
raise error.ProtocolError(
5154
'Unknown auth protocol %s' % (authProtocol,)
5255
)
56+
return localkey.hashPassphrase(privKey, hashAlgo)
5357

5458
# 2.1
5559
def localizeKey(self, authProtocol, privKey, snmpEngineID):
5660
if authProtocol == hmacmd5.HmacMd5.serviceID:
57-
localPrivKey = localkey.localizeKeyMD5(privKey, snmpEngineID)
58-
# now extend this key if too short by repeating steps that includes the hashPassphrase step
59-
while len(localPrivKey) < self.keySize:
60-
newKey = localkey.hashPassphraseMD5(localPrivKey)
61-
localPrivKey += localkey.localizeKeyMD5(newKey, snmpEngineID)
61+
hashAlgo = md5
6262
elif authProtocol == hmacsha.HmacSha.serviceID:
63-
localPrivKey = localkey.localizeKeySHA(privKey, snmpEngineID)
64-
while len(localPrivKey) < self.keySize:
65-
newKey = localkey.hashPassphraseSHA(localPrivKey)
66-
localPrivKey += localkey.localizeKeySHA(newKey, snmpEngineID)
63+
hashAlgo = sha1
64+
elif authProtocol in hmacsha2.HmacSha2.hashAlgo:
65+
hashAlgo = hmacsha2.HmacSha2.hashAlgo[authProtocol]
6766
else:
6867
raise error.ProtocolError(
6968
'Unknown auth protocol %s' % (authProtocol,)
7069
)
70+
localPrivKey = localkey.localizeKey(privKey, snmpEngineID, hashAlgo)
7171
return localPrivKey[:self.keySize]
7272

7373
# 5.1.1.1

pysnmp/proto/secmod/rfc3414/auth/base.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ def hashPassphrase(self, authKey):
1515

1616
def localizeKey(self, authKey, snmpEngineID):
1717
raise error.ProtocolError(errind.noAuthentication)
18+
19+
def getTagLen(self):
20+
raise error.ProtocolError(errind.noAuthentication)
1821

1922
# 7.2.4.1
2023
def authenticateOutgoingMsg(self, authKey, wholeMsg):

pysnmp/proto/secmod/rfc3414/auth/hmacmd5.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ def hashPassphrase(self, authKey):
3232
def localizeKey(self, authKey, snmpEngineID):
3333
return localkey.localizeKeyMD5(authKey, snmpEngineID)
3434

35+
def getTagLen(self):
36+
return 12
37+
3538
# 6.3.1
3639
def authenticateOutgoingMsg(self, authKey, wholeMsg):
3740
# Here we expect calling secmod to indicate where the digest

0 commit comments

Comments
 (0)