cti-python-stix2/stix2/hashes.py

96 lines
2.6 KiB
Python

"""
Library support for hash algorithms, independent of STIX specs.
"""
import enum
import re
class Hash(enum.Enum):
"""
Instances represent a hash algorithm, independent of STIX spec version.
Different spec versions may have different requirements for naming; this
allows us to refer to and use hash algorithms in a spec-agnostic way.
"""
MD5 = 0
MD6 = 1
RIPEMD160 = 2
SHA1 = 3
SHA224 = 4
SHA256 = 5
SHA384 = 6
SHA512 = 7
SHA3224 = 8
SHA3256 = 9
SHA3384 = 10
SHA3512 = 11
SSDEEP = 12
WHIRLPOOL = 13
TLSH = 14
# Regexes used to sanity check hash values. Could also be combined with the
# enum values themselves using enum definition tricks, but... this seems
# simpler.
_HASH_REGEXES = {
Hash.MD5: r"^[a-f0-9]{32}$",
Hash.MD6: r"^[a-f0-9]{32}|[a-f0-9]{40}|[a-f0-9]{56}|[a-f0-9]{64}|[a-f0-9]{96}|[a-f0-9]{128}$",
Hash.RIPEMD160: r"^[a-f0-9]{40}$",
Hash.SHA1: r"^[a-f0-9]{40}$",
Hash.SHA224: r"^[a-f0-9]{56}$",
Hash.SHA256: r"^[a-f0-9]{64}$",
Hash.SHA384: r"^[a-f0-9]{96}$",
Hash.SHA512: r"^[a-f0-9]{128}$",
Hash.SHA3224: r"^[a-f0-9]{56}$",
Hash.SHA3256: r"^[a-f0-9]{64}$",
Hash.SHA3384: r"^[a-f0-9]{96}$",
Hash.SHA3512: r"^[a-f0-9]{128}$",
Hash.SSDEEP: r"^[a-z0-9/+:.]{1,128}$",
Hash.WHIRLPOOL: r"^[a-f0-9]{128}$",
Hash.TLSH: r"^[a-f0-9]{70}$",
}
# compile all the regexes; be case-insensitive
for hash_, re_str in _HASH_REGEXES.items():
_HASH_REGEXES[hash_] = re.compile(re_str, re.I)
def infer_hash_algorithm(name):
"""
Given a hash algorithm name, try to figure out which hash algorithm it
refers to. This primarily enables some user flexibility in naming hash
algorithms when creating STIX content.
:param name: A hash algorithm name
:return: A Hash enum value if the name was recognized, or None if it was
not recognized.
"""
enum_name = name.replace("-", "").upper()
try:
enum_obj = Hash[enum_name]
except KeyError:
enum_obj = None
return enum_obj
def check_hash(hash_, value):
"""
Sanity check the given hash value, against the given hash algorithm.
:param hash_: The hash algorithm, as one of the Hash enums
:param value: A hash value as string
:return: True if the value seems okay; False if not
"""
# I guess there's no need to require a regex mapping for the algorithm...
# Just assume it's okay if we have no way to check it.
result = True
regex = _HASH_REGEXES.get(hash_)
if regex:
result = bool(regex.match(value))
return result