aboutsummaryrefslogtreecommitdiff
path: root/test/functional/test_framework/crypto/muhash.py
blob: 09241f620370ec38619a06be7a76d7e8c40ffb3d (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
# Copyright (c) 2020 Pieter Wuille
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Native Python MuHash3072 implementation."""

import hashlib
import unittest

from .chacha20 import chacha20_block

def data_to_num3072(data):
    """Hash a 32-byte array data to a 3072-bit number using 6 Chacha20 operations."""
    bytes384 = b""
    for counter in range(6):
        bytes384 += chacha20_block(data, bytes(12), counter)
    return int.from_bytes(bytes384, 'little')

class MuHash3072:
    """Class representing the MuHash3072 computation of a set.

    See https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf and https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014337.html
    """

    MODULUS = 2**3072 - 1103717

    def __init__(self):
        """Initialize for an empty set."""
        self.numerator = 1
        self.denominator = 1

    def insert(self, data):
        """Insert a byte array data in the set."""
        data_hash = hashlib.sha256(data).digest()
        self.numerator = (self.numerator * data_to_num3072(data_hash)) % self.MODULUS

    def remove(self, data):
        """Remove a byte array from the set."""
        data_hash = hashlib.sha256(data).digest()
        self.denominator = (self.denominator * data_to_num3072(data_hash)) % self.MODULUS

    def digest(self):
        """Extract the final hash. Does not modify this object."""
        val = (self.numerator * pow(self.denominator, -1, self.MODULUS)) % self.MODULUS
        bytes384 = val.to_bytes(384, 'little')
        return hashlib.sha256(bytes384).digest()

class TestFrameworkMuhash(unittest.TestCase):
    def test_muhash(self):
        muhash = MuHash3072()
        muhash.insert(b'\x00' * 32)
        muhash.insert((b'\x01' + b'\x00' * 31))
        muhash.remove((b'\x02' + b'\x00' * 31))
        finalized = muhash.digest()
        # This mirrors the result in the C++ MuHash3072 unit test
        self.assertEqual(finalized[::-1].hex(), "10d312b100cbd32ada024a6646e40d3482fcff103668d2625f10002a607d5863")