summaryrefslogtreecommitdiff
path: root/pysnmp/proto/secmod/rfc7860/auth/hmacsha2.py
blob: 495f8fcd71c09e29cd62bb4c6db60d1138665e8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#
# This file is part of pysnmp software.
#
# Copyright (c) 2005-2018, Olivier Verriest <verri@x25.pm>
# License: http://snmplabs.com/pysnmp/license.html
#
import sys
import hmac
try:
    from hashlib import sha224, sha256, sha384, sha512

except ImportError:

    class NotAvailable(object):
        def __call__(self, *args, **kwargs):
            raise errind.authenticationError

    sha224 = sha256 = sha384 = sha512 = NotAvailable()

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):
    SHA224_SERVICE_ID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 4)  # usmHMAC128SHA224AuthProtocol
    SHA256_SERVICE_ID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 5)  # usmHMAC192SHA256AuthProtocol
    SHA384_SERVICE_ID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 6)  # usmHMAC256SHA384AuthProtocol
    SHA512_SERVICE_ID = (1, 3, 6, 1, 6, 3, 10, 1, 1, 7)  # usmHMAC384SHA512AuthProtocol

    KEY_LENGTH = {
        SHA224_SERVICE_ID: 28,
        SHA256_SERVICE_ID: 32,
        SHA384_SERVICE_ID: 48,
        SHA512_SERVICE_ID: 64
    }

    DIGEST_LENGTH = {
        SHA224_SERVICE_ID: 16,
        SHA256_SERVICE_ID: 24,
        SHA384_SERVICE_ID: 32,
        SHA512_SERVICE_ID: 48
    }

    HASH_ALGORITHM = {
        SHA224_SERVICE_ID: sha224,
        SHA256_SERVICE_ID: sha256,
        SHA384_SERVICE_ID: sha384,
        SHA512_SERVICE_ID: sha512
    }
    
    IPAD = [0x36] * 64
    OPAD = [0x5C] * 64
    
    def __init__(self, oid):
        if oid not in self.HASH_ALGORITHM:
            raise error.ProtocolError('No SHA-2 authentication algorithm %s available' % (oid,))
        self.__hashAlgo = self.HASH_ALGORITHM[oid]
        self.__digestLength = self.DIGEST_LENGTH[oid]
        self.__placeHolder = univ.OctetString((0,) * self.__digestLength).asOctets()

    def hashPassphrase(self, authKey):
        return localkey.hashPassphrase(authKey, self.__hashAlgo)

    def localizeKey(self, authKey, snmpEngineID):
        return localkey.localizeKey(authKey, snmpEngineID, self.__hashAlgo)

    @property
    def digestLength(self):
        return self.__digestLength

    # 7.3.1
    def authenticateOutgoingMsg(self, authKey, wholeMsg):
        # 7.3.1.1
        location = wholeMsg.find(self.__placeHolder)
        if location == -1:
            raise error.ProtocolError('Can\'t locate digest placeholder')
        wholeHead = wholeMsg[:location]
        wholeTail = wholeMsg[location + self.__digestLength:]

        # 7.3.1.2, 7.3.1.3
        try:
            mac = hmac.new(authKey.asOctets(), wholeMsg, self.__hashAlgo)

        except errind.ErrorIndication as exc:
            raise error.StatusInformation(errorIndication=exc)

        # 7.3.1.4
        mac = mac.digest()[:self.__digestLength]

        # 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.__digestLength:
            raise error.StatusInformation(
                errorIndication=errind.authenticationError
            )

        # 7.3.2.3
        location = wholeMsg.find(authParameters.asOctets())
        if location == -1:
            raise error.ProtocolError('Can\'t locate digest in wholeMsg')
        wholeHead = wholeMsg[:location]
        wholeTail = wholeMsg[location + self.__digestLength:]
        authenticatedWholeMsg = wholeHead + self.__placeHolder + wholeTail

        # 7.3.2.4
        try:
            mac = hmac.new(authKey.asOctets(), authenticatedWholeMsg, self.__hashAlgo)

        except errind.ErrorIndication as exc:
            raise error.StatusInformation(errorIndication=exc)

        # 7.3.2.5
        mac = mac.digest()[:self.__digestLength]

        # 7.3.2.6
        if mac != authParameters:
            raise error.StatusInformation(
                errorIndication=errind.authenticationFailure
            )

        return authenticatedWholeMsg