1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
5# 2015, 2016 Maxence Tury
6
7"""
8Hash classes.
9"""
10
11from scapy.config import conf, crypto_validator
12from scapy.layers.tls.crypto.md4 import MD4 as md4
13
14if conf.crypto_valid:
15 from cryptography.hazmat.backends import default_backend
16 from cryptography.hazmat.primitives import hashes
17 from cryptography.hazmat.primitives.hashes import (
18 MD5,
19 SHA1,
20 SHA224,
21 SHA256,
22 SHA384,
23 SHA512,
24 SHAKE256,
25 )
26 from cryptography.hazmat.primitives.hashes import HashAlgorithm
27else:
28 MD5 = SHA1 = SHA224 = SHA256 = SHA384 = SHA512 = SHAKE256 = None
29 HashAlgorithm = object
30
31_tls_hash_algs = {}
32
33
34class _GenericHashMetaclass(type):
35 """
36 Hash classes are automatically registered through this metaclass.
37 Furthermore, their name attribute is extracted from their class name.
38 """
39
40 def __new__(cls, hash_name, bases, dct):
41 if hash_name != "_GenericHash":
42 dct["name"] = hash_name[5:].lower() # remove leading "Hash_"
43 the_class = super(_GenericHashMetaclass, cls).__new__(
44 cls, hash_name, bases, dct
45 )
46 if hash_name != "_GenericHash":
47 _tls_hash_algs[dct["name"]] = the_class
48 return the_class
49
50
51class _GenericHash(metaclass=_GenericHashMetaclass):
52 def digest(self, tbd):
53 digest = hashes.Hash(self.hash_cls(), backend=default_backend())
54 digest.update(tbd)
55 return digest.finalize()
56
57
58class Hash_NULL(_GenericHash):
59 hash_len = 0
60
61 def digest(self, tbd):
62 return b""
63
64
65class Hash_MD4(_GenericHash):
66 hash_cls = md4
67 hash_len = 16
68
69 def digest(self, tbd):
70 return self.hash_cls(tbd).digest()
71
72
73class Hash_MD5(_GenericHash):
74 hash_cls = MD5
75 hash_len = 16
76
77
78class Hash_SHA(_GenericHash):
79 hash_cls = SHA1
80 hash_len = 20
81
82
83_tls_hash_algs["sha1"] = Hash_SHA
84
85
86class Hash_SHA224(_GenericHash):
87 hash_cls = SHA224
88 hash_len = 28
89
90
91class Hash_SHA256(_GenericHash):
92 hash_cls = SHA256
93 hash_len = 32
94
95
96class Hash_SHA384(_GenericHash):
97 hash_cls = SHA384
98 hash_len = 48
99
100
101class Hash_SHA512(_GenericHash):
102 hash_cls = SHA512
103 hash_len = 64
104
105
106# first, we add the "md5-sha1" hash from openssl to python-cryptography
107class MD5_SHA1(HashAlgorithm):
108 name = "md5-sha1"
109 digest_size = 36
110 block_size = 64
111
112
113class Hash_MD5SHA1(_GenericHash):
114 hash_cls = MD5_SHA1
115 hash_len = 36
116
117
118_tls_hash_algs["md5-sha1"] = Hash_MD5SHA1
119
120
121class Hash_SHAKE256(_GenericHash):
122 hash_cls = SHAKE256
123
124 def __init__(self, digest_size: int):
125 self.hash_len = digest_size
126
127 def digest(self, tbd):
128 digest = hashes.Hash(self.hash_cls(self.hash_len), backend=default_backend())
129 digest.update(tbd)
130 return digest.finalize()
131
132
133@crypto_validator
134def _get_hash(hashStr):
135 """
136 Return a cryptography-hash by its name
137 """
138 try:
139 return _tls_hash_algs[hashStr].hash_cls()
140 except KeyError:
141 raise KeyError("Unknown hash function %s" % hashStr)