Valtteri Niemi

112 papers A* 5A 1B 9C 10Misc 8Journal 43Unranked 32
YearRankTypeTitle / Venue / Authors
2026 conf
ICISSP (2)
Gizem Akman, Kuan Eeik Tan, Valtteri Niemi
2026 J jnl
CoRR
Arthur Nijdam, Harri Kähkönen, Valtteri Niemi, Paul Stankovski Wagner, Sara Ramezanian
2025 conf
ESORICS Workshops (3)
Gizem Akman, Philip Ginzboorg, Sampo Sovio, Valtteri Niemi
2025 J jnl
IEEE Netw.
Zheng Yan, Tieyan Li, Valtteri Niemi, Elisa Bertino
2024 J jnl
Theor. Comput. Sci.
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2024 J jnl
IEEE Access
Sara Ramezanian, Valtteri Niemi
2023 J jnl
Comput.
Gizem Akman, Philip Ginzboorg, Mohamed Taoufiq Damir, Valtteri Niemi
2022 B conf
NSS
Mohamed Taoufiq Damir, Tommi Meskanen, Sara Ramezanian, Valtteri Niemi
2022 conf
ICCSA (Workshops 4)
Gizem Akman, Philip Ginzboorg, Valtteri Niemi
2022 conf
CSCS
Sara Ramezanian, Gizem Akman, Mohamed Taoufiq Damir, Valtteri Niemi
2022 conf
NordSec
Mohamed Taoufiq Damir, Valtteri Niemi
2022 conf
HPCC/DSS/SmartCity/DependSys
Limin Xiao, Nobuyoshi Asai, Valtteri Niemi
2022 conf
WISEC
Mohamed Taoufiq Damir, Valtteri Niemi
2022 J jnl
CoRR
Mohamed Taoufiq Damir, Tommi Meskanen, Sara Ramezanian, Valtteri Niemi
2022 J jnl
Network
Gizem Akman, Philip Ginzboorg, Valtteri Niemi
2022 Misc conf
FRUCT
Tommi Meskanen, Jarkko Kuusijärvi, Sara Ramezanian, Valtteri Niemi
2021 ed.
Blockchain
Yang Xiang, Ziyuan Wang, Honggang Wang, Valtteri Niemi
2021 J jnl
IEEE Commun. Stand. Mag.
Mohsin Khan, Philip Ginzboorg, Valtteri Niemi
2021 J jnl
IEEE Trans. Very Large Scale Integr. Syst.
Milad Bahadori, Kimmo Järvinen, Valtteri Niemi
2021 B conf
DBSec
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2021 J jnl
CoRR
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2021 Misc conf
FRUCT
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2021 J jnl
Int. J. Embed. Real Time Commun. Syst.
Tommi Meskanen, Jarkko Kuusijärvi, Valtteri Niemi
2021 conf
ICCSA (8)
Gizem Akman, Philip Ginzboorg, Valtteri Niemi
2020 J jnl
Int. J. Embed. Real Time Commun. Syst.
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2020 Misc conf
FRUCT
Tommi Meskanen, Valtteri Niemi, Jarkko Kuusijäarvi
2020 J jnl
Digit. Commun. Networks
Sara Ramezanian, Tommi Meskanen, Masoud Naderpour, Ville Junnila, Valtteri Niemi
2019 Misc conf
FRUCT
Gizem Akman, Valtteri Niemi, Philip Ginzboorg
2019 conf
SSR
Mohsin Khan, Philip Ginzboorg, Valtteri Niemi
2019 Misc conf
FRUCT
Sara Ramezanian, Valtteri Niemi
2019 Misc conf
SAC
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2019 J jnl
Int. J. Embed. Real Time Commun. Syst.
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2018 conf
SSR
Mohsin Khan, Philip Ginzboorg, Kimmo Järvinen, Valtteri Niemi
2018 J jnl
CoRR
Mohsin Khan, Philip Ginzboorg, Kimmo Järvinen, Valtteri Niemi
2018 conf
SPAWC
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2018 J jnl
IEEE Access
Zheng Yan, Honggang Wang, Laurence T. Yang, Valtteri Niemi
2018 conf
NTMS
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2018 Misc conf
FRUCT
Sara Ramezanian, Tommi Meskanen, Valtteri Niemi
2017 conf
5GWN
Mohsin Khan, Valtteri Niemi
2017 J jnl
CoRR
Mohsin Khan, Valtteri Niemi
2017 J jnl
J. ICT Stand.
Masoud Naderpour, Tommi Meskanen, Andrew Paverd, Valtteri Niemi
2017 B conf
NSS
Mohsin Khan, Valtteri Niemi
2017 J jnl
CoRR
Mohsin Khan, Valtteri Niemi
2017 B conf
WI
Ville Ollikainen, Valtteri Niemi
2017 B conf
NSS
Tommi Meskanen, Masoud Naderpour, Valtteri Niemi
2017 Misc conf
ICISS
Mohsin Khan, Kimmo Järvinen, Philip Ginzboorg, Valtteri Niemi
2017 J jnl
J. ICT Stand.
Mohsin Khan, Valtteri Niemi
2017 B conf
NSS
Sara Ramezanian, Tommi Meskanen, Masoud Naderpour, Valtteri Niemi
2017 J jnl
Mob. Networks Appl.
Mingjun Wang, Zheng Yan, Valtteri Niemi
2016 J jnl
Int. J. Commun. Syst.
Zheng Yan, Valtteri Niemi, Laurence T. Yang
2016 A* conf
NDSS
Altaf Shaik, Jean-Pierre Seifert, Ravishankar Borgaonkar, N. Asokan, Valtteri Niemi
2016 conf
MobiMedia
Philip Ginzboorg, Valtteri Niemi
2016 J jnl
Future Gener. Comput. Syst.
Zheng Yan, Wenxiu Ding, Valtteri Niemi, Athanasios V. Vasilakos
2015 conf
S3@MobiCom
Ibbad Hafeez, Aaron Yi Ding, Lauri Suomalainen, Seppo Hätönen, Valtteri Niemi, Sasu Tarkoma
2015 conf
TrustCom/BigDataSE/ISPA (1)
Tommi Meskanen, Valtteri Niemi, Noora Nieminen
2015 J jnl
J. Cyber Secur. Mobil.
Tommi Meskanen, Valtteri Niemi, Noora Nieminen
2015 J jnl
CoRR
Altaf Shaik, Ravishankar Borgaonkar, N. Asokan, Valtteri Niemi, Jean-Pierre Seifert
2015 conf
TrustCom/BigDataSE/ISPA (1)
Tommi Meskanen, Jian Liu, Sara Ramezanian, Valtteri Niemi
2014 J jnl
Comput. Secur.
Zheng Yan, Guojun Wang, Valtteri Niemi, Robert H. Deng
2014 B conf
TrustCom
Tommi Meskanen, Valtteri Niemi, Noora Nieminen
2014 J jnl
Comput. Commun.
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2014 C conf
CIT
Tommi Meskanen, Valtteri Niemi, Noora Nieminen
2013 conf
TrustCom/ISPA/IUCC
Zheng Yan, Peng Zhang, Valtteri Niemi, Raimo Kantola
2013 J jnl
Pers. Ubiquitous Comput.
Zheng Yan, Conghui Liu, Valtteri Niemi, Guoliang Yu
2013 J jnl
Comput. Commun.
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2013 conf
WPES
Igor Bilogrevic, Kévin Huguenin, Murtuza Jadliwala, Florent Lopez, Jean-Pierre Hubaux, Philip Ginzboorg, Valtteri Niemi
2013 conf
UbiComp (Adjunct Publication)
Igor Bilogrevic, Kévin Huguenin, Murtuza Jadliwala, Florent Lopez, Jean-Pierre Hubaux, Philip Ginzboorg, Valtteri Niemi
2013 J jnl
Mob. Networks Appl.
Julien Freudiger, Murtuza Jadliwala, Jean-Pierre Hubaux, Valtteri Niemi, Philip Ginzboorg
2013 conf
CNS
Zheng Yan, Mingjun Wang, Valtteri Niemi, Raimo Kantola
2012 conf
Pervasive
Igor Bilogrevic, Murtuza Jadliwala, István Lám, Imad Aad, Philip Ginzboorg, Valtteri Niemi, Laurent Bindschaedler, Jean-Pierre Hubaux
2012 J jnl
Serv. Oriented Comput. Appl.
Debmalya Biswas, Nikolai Nefedov, Valtteri Niemi
2012 C conf
WOWMOM
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2012 A* conf
NDSS
Laurent Bindschaedler, Murtuza Jadliwala, Igor Bilogrevic, Imad Aad, Philip Ginzboorg, Valtteri Niemi, Jean-Pierre Hubaux
2011 conf
ANT/MobiWIS
Debmalya Biswas, Nikolai Nefedov, Valtteri Niemi
2011 B conf
TrustCom
Yu Chen, Zheng Yan, Valtteri Niemi
2011 J jnl
J. Syst. Softw.
Igor Bilogrevic, Murtuza Jadliwala, Praveen Kumar, Sudeep Singh Walia, Jean-Pierre Hubaux, Imad Aad, Valtteri Niemi
2011 conf
EW
Philip Ginzboorg, Valtteri Niemi, Jörg Ott
2011 B conf
TrustCom
Valtteri Niemi
2011 conf
CODASPY
Igor Bilogrevic, Murtuza Jadliwala, Jean-Pierre Hubaux, Imad Aad, Valtteri Niemi
2011 C conf
WOWMOM
Murtuza Jadliwala, Julien Freudiger, Imad Aad, Jean-Pierre Hubaux, Valtteri Niemi
2011 conf
HASE
Debmalya Biswas, Valtteri Niemi
2010 J jnl
Comput. Commun.
Valtteri Niemi
2010 C conf
ATC
Zheng Yan, Conghui Liu, Valtteri Niemi, Guoliang Yu
2010 J jnl
J. Univers. Comput. Sci.
Philip Ginzboorg, Valtteri Niemi
2009 C conf
ATC
Zheng Yan, Valtteri Niemi
2009 conf
INTRUST
Zheng Yan, Yan Dong, Valtteri Niemi, Guoliang Yu
2008 C conf
ATC
Zheng Yan, Valtteri Niemi, Yan Dong, Guoliang Yu
2004 conf
Theory Is Forever
Valtteri Niemi
2004 J jnl
Inf. Secur. Tech. Rep.
Valtteri Niemi
2003 C conf
IMACC
Valtteri Niemi
2003 conf
Grammars and Automata for String Processing
Valtteri Niemi
2003 conf
Security Protocols Workshop
N. Asokan, Valtteri Niemi, Kaisa Nyberg
2003 book
UMTS security.
Valtteri Niemi, Kaisa Nyberg
2002 J jnl
IACR Cryptol. ePrint Arch.
N. Asokan, Valtteri Niemi, Kaisa Nyberg
2002 conf
Financial Cryptography
Helger Lipmaa, N. Asokan, Valtteri Niemi
2001 J jnl
IACR Cryptol. ePrint Arch.
Helger Lipmaa, N. Asokan, Valtteri Niemi
1999 J jnl
Theor. Comput. Sci.
Valtteri Niemi, Ari Renvall
1999 J jnl
Fundam. Informaticae
Valtteri Niemi, Ari Renvall
1998 J jnl
Theor. Comput. Sci.
Valtteri Niemi, Ari Renvall
1997 ch.
Handbook of Formal Languages (2)
Valtteri Niemi
1997 C conf
Developments in Language Theory
Valtteri Niemi, Ari Renvall
1997 A* conf
FSE
Cunsheng Ding, Valtteri Niemi, Ari Renvall, Arto Salomaa
1995 C conf
Developments in Language Theory
Jarkko Kari, Valtteri Niemi
1994 conf
Results and Trends in Theoretical Computer Science
Valtteri Niemi, Ari Renvall
1994 A conf
ASIACRYPT
Valtteri Niemi, Ari Renvall
1993 ch.
Current Trends in Theoretical Computer Science
Valtteri Niemi
1993 C conf
Developments in Language Theory
Jarkko Kari, Valtteri Niemi
1991 J jnl
Bull. EATCS
Valtteri Niemi
1991 J jnl
Discret. Appl. Math.
Valtteri Niemi
1990 A* conf
EUROCRYPT
Valtteri Niemi
1988 A* conf
ICALP
Valtteri Niemi
1984 J jnl
Theor. Comput. Sci.
Valtteri Niemi
tests/integration/test_hashes.py
← Index tests/integration/test_hashes.py python
"""
Integration tests for the HashExtractor class.
"""
import pytest
import hashlib
from unittest.mock import Mock, patch, MagicMock

pytestmark = [pytest.mark.integration]


class TestHashExtractorInitialization:
    """Tests for HashExtractor initialization."""

    def test_initialization_elf(self, elf_binary_path, mock_logger):
        """Test initialization with ELF file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.filepath == elf_binary_path
            assert extractor.hashes is None
            assert extractor.filetype == "elf"
            assert extractor.macho is None

    def test_initialization_with_macho_object(self, elf_binary_path, mock_logger):
        """Test initialization with pre-parsed macho object."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)

            assert extractor.macho is mock_macho

    def test_initialization_pe(self, pe_binary_path, mock_logger):
        """Test initialization with PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            assert extractor.filepath == pe_binary_path
            assert extractor.filetype == "pebin"
            # PE object should be created
            assert extractor.pe is not None

    def test_elastic_index_suffix(self, elf_binary_path, mock_logger):
        """Test that elastic_index has correct suffix."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert "-hashes" in extractor.elastic_index


class TestHashExtractorTag:
    """Tests for tag method."""

    def test_tag_value(self, elf_binary_path, mock_logger):
        """Test that tag returns correct value."""
        from redb.extractors.hashes import HashExtractor
        from redb.extractors.enum import Tag

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.tag() == Tag.HASH.value
            assert extractor.tag() == "hash"


class TestHashExtractorBasicHashes:
    """Tests for basic hash extraction."""

    def test_extract_md5(self, elf_binary_path, mock_logger, known_hashes):
        """Test MD5 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.md5 == known_hashes['md5']

    def test_extract_sha1(self, elf_binary_path, mock_logger, known_hashes):
        """Test SHA1 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.sha1 == known_hashes['sha1']

    def test_extract_sha256(self, elf_binary_path, mock_logger, known_hashes):
        """Test SHA256 hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.sha256 == known_hashes['sha256']

    def test_extract_ssdeep(self, elf_binary_path, mock_logger):
        """Test ssdeep hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # ssdeep should be a string
            assert isinstance(result.ssdeep_hash, str)

    def test_extract_tlsh(self, elf_binary_path, mock_logger):
        """Test TLSH hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # TLSH should be a string
            assert isinstance(result.tlsh_hash, str)


class TestHashExtractorPEHashes:
    """Tests for PE-specific hash extraction."""

    def test_extract_imphash(self, pe_binary_path, mock_logger):
        """Test imphash extraction for PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Imphash should be set for PE files
            if extractor.pe is not None:
                assert result.imphash is not None or result.imphash is None  # May or may not have imports

    def test_extract_authentihash(self, pe_binary_path, mock_logger):
        """Test authentihash extraction for PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Authentihash should be set for PE files
            if extractor.pe is not None:
                assert result.authentihash is not None

    def test_pe_hashes_not_set_for_elf(self, elf_binary_path, mock_logger):
        """Test that PE-specific hashes are not set for ELF files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # PE-specific hashes should be None for ELF
            assert result.imphash is None
            assert result.authentihash is None
            assert result.richhash is None


class TestHashExtractorRichHeaderHashes:
    """Tests for Rich header hash extraction."""

    def test_compute_richhash(self, pe_binary_path, mock_logger):
        """Test Rich header hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Rich hash may or may not be present depending on the PE
            if result.richhash is not None:
                assert isinstance(result.richhash, str)
                assert len(result.richhash) == 32  # MD5 hex length

    def test_compute_richpe_hash(self, pe_binary_path, mock_logger):
        """Test RichPE hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # RichPE hash may or may not be present
            if result.richpe_hash is not None:
                assert isinstance(result.richpe_hash, str)
                assert len(result.richpe_hash) == 32  # MD5 hex length

    def test_compute_richpv_hash(self, pe_binary_path, mock_logger):
        """Test RichPV hash computation."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # RichPV hashes may or may not be present
            if result.richpv_hash is not None:
                assert isinstance(result.richpv_hash, str)
            if result.richpv_hash_sorted is not None:
                assert isinstance(result.richpv_hash_sorted, str)


class TestHashExtractorELFHashes:
    """Tests for ELF-specific hash extraction."""

    def test_extract_elf_import_hash(self, elf_binary_path, mock_logger):
        """Test ELF import hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Import hash may or may not be present
            if result.import_hash is not None:
                assert isinstance(result.import_hash, str)
                assert len(result.import_hash) == 32

    def test_extract_elf_export_hash(self, elf_binary_path, mock_logger):
        """Test ELF export hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Export hash may or may not be present
            if result.export_hash is not None:
                assert isinstance(result.export_hash, str)

    def test_extract_elf_section_hash(self, elf_binary_path, mock_logger):
        """Test ELF section hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Section hash should be present for valid ELF
            if result.section_hash is not None:
                assert isinstance(result.section_hash, str)

    def test_extract_elf_symbol_hash(self, elf_binary_path, mock_logger):
        """Test ELF symbol hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Symbol hash (symhash) may or may not be present
            if result.symbol_hash is not None:
                assert isinstance(result.symbol_hash, str)

    def test_extract_elf_dynamic_hash(self, elf_binary_path, mock_logger):
        """Test ELF dynamic hash extraction."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            # Dynamic hash may or may not be present
            if result.dynamic_hash is not None:
                assert isinstance(result.dynamic_hash, str)


class TestHashExtractorPrepareExportData:
    """Tests for prepare_export_data method."""

    def test_prepare_export_elasticsearch(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for Elasticsearch."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ElasticsearchExporter")

            assert result is extractor.hashes

    def test_prepare_export_clickhouse(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            # Should return tuple of (data, column_names, column_type_names)
            assert isinstance(result, tuple)
            assert len(result) == 3

            data, column_names, column_type_names = result

            assert isinstance(data, list)
            assert isinstance(column_names, list)
            assert isinstance(column_type_names, list)

            # Check expected columns
            assert 'sha256' in column_names
            assert 'md5' in column_names
            assert 'sha1' in column_names
            assert 'ssdeep_hash' in column_names
            assert 'tlsh_hash' in column_names

    def test_prepare_export_clickhouse_pe(self, pe_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse with PE file."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            data, column_names, column_type_names = result

            # PE-specific columns should be present
            assert 'authentihash' in column_names
            assert 'imphash' in column_names
            assert 'richhash' in column_names

    def test_prepare_export_clickhouse_has_macho_columns(self, elf_binary_path, mock_logger):
        """Test prepare_export_data for ClickHouse includes Mach-O hash columns."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)
            extractor.extract()

            result = extractor.prepare_export_data("ClickHouseExporter")

            data, column_names, column_type_names = result

            # Mach-O hash columns should be present
            assert 'macho_dylib_hash' in column_names
            assert 'macho_import_hash' in column_names
            assert 'macho_export_hash' in column_names
            assert 'macho_entitlement_hash' in column_names
            assert 'macho_symhash' in column_names


class TestHashExtractorClickHouseTable:
    """Tests for get_clickhouse_table method."""

    def test_clickhouse_table_name(self, elf_binary_path, mock_logger):
        """Test correct ClickHouse table name."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            assert extractor.get_clickhouse_table() == "redb_hashes"


@pytest.mark.unit
@pytest.mark.dataclass
class TestHashExtractorHashesDataclass:
    """Tests for Hashes dataclass structure."""

    def test_hashes_dataclass_structure(self, elf_binary_path, mock_logger):
        """Test that Hashes dataclass has expected structure."""
        from redb.extractors.hashes import HashExtractor
        from redb.models.dataclasses import Hashes

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert isinstance(result, Hashes)
            # Required fields
            assert hasattr(result, 'md5')
            assert hasattr(result, 'sha1')
            assert hasattr(result, 'sha256')
            assert hasattr(result, 'ssdeep_hash')
            assert hasattr(result, 'tlsh_hash')
            # Optional fields
            assert hasattr(result, 'authentihash')
            assert hasattr(result, 'imphash')
            assert hasattr(result, 'richhash')

    def test_hashes_dataclass_has_macho_fields(self):
        """Test that Hashes dataclass has Mach-O hash fields."""
        from redb.models.dataclasses import Hashes

        # Create instance with default values
        hashes = Hashes(
            md5="d41d8cd98f00b204e9800998ecf8427e",
            sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709",
            sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            ssdeep_hash="",
            tlsh_hash=""
        )

        # Verify Mach-O hash fields exist and default to None
        assert hasattr(hashes, 'macho_dylib_hash')
        assert hasattr(hashes, 'macho_import_hash')
        assert hasattr(hashes, 'macho_export_hash')
        assert hasattr(hashes, 'macho_entitlement_hash')
        assert hasattr(hashes, 'macho_symhash')

        assert hashes.macho_dylib_hash is None
        assert hashes.macho_import_hash is None
        assert hashes.macho_export_hash is None
        assert hashes.macho_entitlement_hash is None
        assert hashes.macho_symhash is None

    def test_hashes_dataclass_with_macho_values(self):
        """Test that Hashes dataclass can be created with Mach-O hash values."""
        from redb.models.dataclasses import Hashes

        hashes = Hashes(
            md5="d41d8cd98f00b204e9800998ecf8427e",
            sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709",
            sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
            ssdeep_hash="",
            tlsh_hash="",
            macho_dylib_hash="abc123def456789012345678901234567",
            macho_import_hash="def456abc123789012345678901234567",
            macho_export_hash="789012abc123def456345678901234567",
            macho_entitlement_hash="345678abc123def456789012901234567",
            macho_symhash="901234abc123def456789012345678567"
        )

        assert hashes.macho_dylib_hash == "abc123def456789012345678901234567"
        assert hashes.macho_import_hash == "def456abc123789012345678901234567"
        assert hashes.macho_export_hash == "789012abc123def456345678901234567"
        assert hashes.macho_entitlement_hash == "345678abc123def456789012901234567"
        assert hashes.macho_symhash == "901234abc123def456789012345678567"


class TestHashExtractorErrorHandling:
    """Tests for error handling in HashExtractor."""

    def test_extract_handles_hash_errors(self, elf_binary_path, mock_logger):
        """Test that extract handles hash computation errors gracefully."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            # Even if some hashes fail, we should get a result
            result = extractor.extract()

            assert result is not None

    def test_extract_with_invalid_pe(self, elf_binary_path, mock_logger):
        """Test extraction when PE parsing fails."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            # Force filetype to be 'pebin' but use ELF file
            with patch.object(HashExtractor, '__init__', lambda self, *args, **kwargs: None):
                extractor = HashExtractor.__new__(HashExtractor)
                extractor.filepath = elf_binary_path
                extractor.log = mock_logger
                extractor.hashes = None
                extractor.pe = None
                extractor.filetype = 'pebin'

                with open(elf_binary_path, 'rb') as f:
                    extractor.binary = f.read()

                # Should not crash
                result = extractor._extract_hashes()
                assert result is not None


class TestHashExtractorMachoHashes:
    """Tests for Mach-O specific hash extraction."""

    def test_macho_hashes_not_set_for_elf(self, elf_binary_path, mock_logger):
        """Test that Mach-O hashes are not set for ELF files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash is None
            assert result.macho_import_hash is None
            assert result.macho_export_hash is None
            assert result.macho_entitlement_hash is None
            assert result.macho_symhash is None

    def test_macho_hashes_not_set_for_pe(self, pe_binary_path, mock_logger):
        """Test that Mach-O hashes are not set for PE files."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(pe_binary_path, mock_logger)

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash is None
            assert result.macho_import_hash is None
            assert result.macho_export_hash is None
            assert result.macho_entitlement_hash is None
            assert result.macho_symhash is None

    def test_extract_macho_hashes_with_mock(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction with mocked macho object."""
        from redb.extractors.hashes import HashExtractor

        # Create mock macho object
        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = {
            'dylib_hash': 'abc123def456789012345678901234ab',
            'import_hash': 'def456abc789012345678901234567cd',
            'export_hash': '789012abc456def345678901234567ef',
            'entitlement_hash': '345678abc123def789012901234567gh',
            'symhash': '901234abc123def456789012345678ij'
        }

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            # Override filetype to trigger macho hash extraction
            extractor.filetype = 'macho'

            result = extractor.extract()

            assert result is not None
            assert result.macho_dylib_hash == 'abc123def456789012345678901234ab'
            assert result.macho_import_hash == 'def456abc789012345678901234567cd'
            assert result.macho_export_hash == '789012abc456def345678901234567ef'
            assert result.macho_entitlement_hash == '345678abc123def789012901234567gh'
            assert result.macho_symhash == '901234abc123def456789012345678ij'

    def test_extract_macho_hashes_fat_binary_mock(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction for FAT binary with mocked macho object."""
        from redb.extractors.hashes import HashExtractor

        # Create mock macho object for FAT binary
        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64", "arm64"]  # Multiple archs = FAT
        mock_macho.get_similarity_hashes.return_value = {
            'fat': {
                'dylib_hash': 'fat_dylib_hash_12345678901234567',
                'import_hash': 'fat_import_hash_12345678901234567',
            },
            'combined': {
                'dylib_hash': 'combined_dylib_hash_1234567890123',
            }
        }

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            assert result is not None
            # Should use 'fat' key for FAT binaries
            assert result.macho_dylib_hash == 'fat_dylib_hash_12345678901234567'
            assert result.macho_import_hash == 'fat_import_hash_12345678901234567'

    def test_extract_macho_hashes_handles_empty(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction handles empty hash dict."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = {}

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            # Should not crash, hashes should be None
            assert result is not None
            assert result.macho_dylib_hash is None

    def test_extract_macho_hashes_handles_none(self, elf_binary_path, mock_logger):
        """Test Mach-O hash extraction handles None return."""
        from redb.extractors.hashes import HashExtractor

        mock_macho = MagicMock()
        mock_macho.get_architectures.return_value = ["x86_64"]
        mock_macho.get_similarity_hashes.return_value = None

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger, macho=mock_macho)
            extractor.filetype = 'macho'

            result = extractor.extract()

            # Should not crash
            assert result is not None


class TestHashExtractorConsistency:
    """Tests for hash consistency."""

    def test_hash_consistency_across_extractions(self, elf_binary_path, mock_logger):
        """Test that hashes are consistent across multiple extractions."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor1 = HashExtractor(elf_binary_path, mock_logger)
            result1 = extractor1.extract()

            extractor2 = HashExtractor(elf_binary_path, mock_logger)
            result2 = extractor2.extract()

            assert result1.md5 == result2.md5
            assert result1.sha1 == result2.sha1
            assert result1.sha256 == result2.sha256

    def test_hash_values_are_lowercase(self, elf_binary_path, mock_logger):
        """Test that hash values are lowercase hex strings."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert result.md5 == result.md5.lower()
            assert result.sha1 == result.sha1.lower()
            assert result.sha256 == result.sha256.lower()

    def test_hash_lengths_correct(self, elf_binary_path, mock_logger):
        """Test that hash lengths are correct."""
        from redb.extractors.hashes import HashExtractor

        with patch('redb.settings.ELASTIC_BINARIES_COLLECTION', 'test'):
            extractor = HashExtractor(elf_binary_path, mock_logger)

            result = extractor.extract()

            assert len(result.md5) == 32  # MD5 = 128 bits = 32 hex chars
            assert len(result.sha1) == 40  # SHA1 = 160 bits = 40 hex chars
            assert len(result.sha256) == 64  # SHA256 = 256 bits = 64 hex chars