1## This file is part of Scapy
2## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
3##                     2015, 2016 Maxence Tury
4## This program is published under a GPLv2 license
5
6"""
7HMAC classes.
8"""
9
10from __future__ import absolute_import
11import hmac
12
13from scapy.layers.tls.crypto.hash import _tls_hash_algs
14import scapy.modules.six as six
15from scapy.compat import *
16
17_SSLv3_PAD1_MD5  = b"\x36"*48
18_SSLv3_PAD1_SHA1 = b"\x36"*40
19_SSLv3_PAD2_MD5  = b"\x5c"*48
20_SSLv3_PAD2_SHA1 = b"\x5c"*40
21
22_tls_hmac_algs = {}
23
24class _GenericHMACMetaclass(type):
25    """
26    HMAC classes are automatically registered through this metaclass.
27    Furthermore, their name attribute is extracted from their class name.
28
29    Note that, when used with TLS, the HMAC key length equates the output of
30    the associated hash function (see RFC 5246, appendix C).
31    Also, we do not need to instantiate the associated hash function.
32    """
33    def __new__(cls, hmac_name, bases, dct):
34        hash_name = hmac_name[5:]               # remove leading "Hmac_"
35        if hmac_name != "_GenericHMAC":
36            dct["name"] = "HMAC-%s" % hash_name
37            dct["hash_alg"] = _tls_hash_algs[hash_name]
38            dct["hmac_len"] = _tls_hash_algs[hash_name].hash_len
39            dct["key_len"] = dct["hmac_len"]
40        the_class = super(_GenericHMACMetaclass, cls).__new__(cls, hmac_name,
41                                                              bases, dct)
42        if hmac_name != "_GenericHMAC":
43            _tls_hmac_algs[dct["name"]] = the_class
44        return the_class
45
46
47class HMACError(Exception):
48    """
49    Raised when HMAC verification fails.
50    """
51    pass
52
53class _GenericHMAC(six.with_metaclass(_GenericHMACMetaclass, object)):
54    def __init__(self, key=None):
55        self.key = key
56
57    def digest(self, tbd):
58        if self.key is None:
59            raise HMACError
60        return hmac.new(raw(self.key), raw(tbd), self.hash_alg.hash_cls).digest()
61
62    def digest_sslv3(self, tbd):
63        if self.key is None:
64            raise HMACError
65
66        h = self.hash_alg()
67        if h.name == "SHA":
68            pad1 = _SSLv3_PAD1_SHA1
69            pad2 = _SSLv3_PAD2_SHA1
70        elif h.name == "MD5":
71            pad1 = _SSLv3_PAD1_MD5
72            pad2 = _SSLv3_PAD2_MD5
73        else:
74            raise HMACError("Provided hash does not work with SSLv3.")
75
76        return h.digest(self.key + pad2 +
77                        h.digest(self.key + pad1 + tbd))
78
79
80class Hmac_NULL(_GenericHMAC):
81    hmac_len = 0
82    key_len = 0
83
84    def digest(self, tbd):
85        return b""
86
87    def digest_sslv3(self, tbd):
88        return b""
89
90class Hmac_MD5(_GenericHMAC):
91    pass
92
93class Hmac_SHA(_GenericHMAC):
94    pass
95
96class Hmac_SHA224(_GenericHMAC):
97    pass
98
99class Hmac_SHA256(_GenericHMAC):
100    pass
101
102class Hmac_SHA384(_GenericHMAC):
103    pass
104
105class Hmac_SHA512(_GenericHMAC):
106    pass
107
108