| """
|
| Toy SHA-520 Variant for Testing and Development
|
|
|
| Reduced-round, reduced-word-size SHA-520 for fast simulation
|
| and verification of cryptanalysis techniques.
|
|
|
| Parameters:
|
| - 4 rounds (not 80)
|
| - 32-bit words (not 64-bit)
|
| - 4-word state (not 8)
|
| - Result: 128-bit hashes (not 512-bit)
|
| """
|
|
|
| import struct
|
| from typing import List, Tuple, Dict, Any
|
| import math
|
|
|
|
|
| class ToySHA520:
|
| """Toy SHA-520 with reduced parameters for fast simulation.
|
|
|
| Maintains SHA structure but reduces complexity for proof-of-concept
|
| attacks (Grover, collision search, etc.).
|
|
|
| Parameters
|
| ----------
|
| rounds : int
|
| Number of compression rounds (typically 4)
|
| word_size : int
|
| Bits per word (typically 32)
|
| n_words : int
|
| Number of state words (typically 4)
|
| """
|
|
|
|
|
| K_toy = [
|
| 0x67452301,
|
| 0xefcdab89,
|
| 0x98badcfe,
|
| 0x10325476,
|
| ]
|
|
|
|
|
| IV_toy = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a]
|
|
|
| def __init__(self, rounds: int = 4) -> None:
|
| """Initialize Toy SHA-520.
|
|
|
| Parameters
|
| ----------
|
| rounds : int
|
| Number of rounds
|
| """
|
| self.rounds = rounds
|
| self.word_size = 32
|
| self.n_words = 4
|
| self.digest_size = (self.n_words * self.word_size) // 8
|
| self.block_size = 64
|
|
|
| self._h = list(self.IV_toy)
|
| self._buffer = b''
|
| self._counter = 0
|
|
|
| @staticmethod
|
| def _rotr(x: int, n: int) -> int:
|
| """Right rotate 32-bit word."""
|
| mask = (1 << 32) - 1
|
| return ((x >> n) | (x << (32 - n))) & mask
|
|
|
| @staticmethod
|
| def _sigma0(x: int) -> int:
|
| """Lower sigma 0 function."""
|
| return ToySHA520._rotr(x, 1) ^ ToySHA520._rotr(x, 5) ^ (x >> 7)
|
|
|
| @staticmethod
|
| def _sigma1(x: int) -> int:
|
| """Lower sigma 1 function."""
|
| return ToySHA520._rotr(x, 11) ^ ToySHA520._rotr(x, 20) ^ (x >> 14)
|
|
|
| @staticmethod
|
| def _Sigma0(x: int) -> int:
|
| """Upper Sigma 0 function."""
|
| return ToySHA520._rotr(x, 2) ^ ToySHA520._rotr(x, 6) ^ ToySHA520._rotr(x, 15)
|
|
|
| @staticmethod
|
| def _Sigma1(x: int) -> int:
|
| """Upper Sigma 1 function."""
|
| return ToySHA520._rotr(x, 7) ^ ToySHA520._rotr(x, 12) ^ ToySHA520._rotr(x, 22)
|
|
|
| @staticmethod
|
| def _Ch(x: int, y: int, z: int) -> int:
|
| """Choice function."""
|
| return (x & y) ^ (~x & z)
|
|
|
| @staticmethod
|
| def _Maj(x: int, y: int, z: int) -> int:
|
| """Majority function."""
|
| return (x & y) ^ (x & z) ^ (y & z)
|
|
|
| def _compress(self, block: bytes) -> None:
|
| """Compress a 64-byte block.
|
|
|
| Parameters
|
| ----------
|
| block : bytes
|
| 64-byte message block
|
| """
|
|
|
| w = list(struct.unpack('>16I', block[:64]))
|
|
|
|
|
| for i in range(8, min(8 + self.rounds, 16)):
|
| s0 = self._sigma0(w[i - 7])
|
| s1 = self._sigma1(w[i - 2])
|
| w.append((w[i - 8] + s0 + w[i - 5] + s1) & 0xffffffff)
|
|
|
|
|
| a, b, c, d = self._h
|
|
|
|
|
| for i in range(self.rounds):
|
| K_idx = i % len(self.K_toy)
|
| w_idx = i % len(w)
|
|
|
| S1 = self._Sigma1(a)
|
| ch = self._Ch(a, b, c)
|
| temp1 = (d + S1 + ch + self.K_toy[K_idx] + w[w_idx]) & 0xffffffff
|
|
|
| S0 = self._Sigma0(a)
|
| maj = self._Maj(a, b, c)
|
| temp2 = (S0 + maj) & 0xffffffff
|
|
|
| d = c
|
| c = b
|
| b = a
|
| a = (temp1 + temp2) & 0xffffffff
|
|
|
|
|
| self._h[0] = (self._h[0] + a) & 0xffffffff
|
| self._h[1] = (self._h[1] + b) & 0xffffffff
|
| self._h[2] = (self._h[2] + c) & 0xffffffff
|
| self._h[3] = (self._h[3] + d) & 0xffffffff
|
|
|
| def update(self, data: bytes) -> None:
|
| """Update hash with data.
|
|
|
| Parameters
|
| ----------
|
| data : bytes
|
| Data to hash
|
| """
|
| if isinstance(data, str):
|
| data = data.encode()
|
|
|
| self._buffer += data
|
| self._counter += len(data)
|
|
|
|
|
| while len(self._buffer) >= self.block_size:
|
| self._compress(self._buffer[:self.block_size])
|
| self._buffer = self._buffer[self.block_size:]
|
|
|
| def finalize(self) -> bytes:
|
| """Finalize hash.
|
|
|
| Returns
|
| -------
|
| bytes
|
| 16-byte (128-bit) digest
|
| """
|
|
|
| h = list(self._h)
|
| buffer = self._buffer
|
| counter = self._counter
|
|
|
|
|
| mdi = counter % self.block_size
|
| length = counter * 8
|
|
|
| if mdi < 56:
|
| padlen = 56 - mdi
|
| else:
|
| padlen = self.block_size + 56 - mdi
|
|
|
| padding = b'\x80' + (b'\x00' * (padlen - 1))
|
| buffer += padding
|
| buffer += struct.pack('>Q', length)
|
|
|
|
|
| temp_h = h
|
|
|
|
|
| for i in range(0, len(buffer), self.block_size):
|
| block = buffer[i:i + self.block_size]
|
| if len(block) == self.block_size:
|
|
|
| w = list(struct.unpack('>16I', block[:64]))
|
|
|
| for j in range(8, 8 + self.rounds):
|
| s0 = self._sigma0(w[j - 7])
|
| s1 = self._sigma1(w[j - 2])
|
| w.append((w[j - 8] + s0 + w[j - 5] + s1) & 0xffffffff)
|
|
|
| a, b, c, d = temp_h
|
|
|
| for j in range(self.rounds):
|
| K_idx = j % len(self.K_toy)
|
| w_idx = j % len(w)
|
|
|
| S1 = self._Sigma1(a)
|
| ch = self._Ch(a, b, c)
|
| temp1 = (d + S1 + ch + self.K_toy[K_idx] + w[w_idx]) & 0xffffffff
|
|
|
| S0 = self._Sigma0(a)
|
| maj = self._Maj(a, b, c)
|
| temp2 = (S0 + maj) & 0xffffffff
|
|
|
| d = c
|
| c = b
|
| b = a
|
| a = (temp1 + temp2) & 0xffffffff
|
|
|
| temp_h[0] = (temp_h[0] + a) & 0xffffffff
|
| temp_h[1] = (temp_h[1] + b) & 0xffffffff
|
| temp_h[2] = (temp_h[2] + c) & 0xffffffff
|
| temp_h[3] = (temp_h[3] + d) & 0xffffffff
|
|
|
| return struct.pack('>4I', *temp_h)
|
|
|
| def digest(self, data: bytes = b'') -> bytes:
|
| """Compute digest.
|
|
|
| Parameters
|
| ----------
|
| data : bytes
|
| Data to hash
|
|
|
| Returns
|
| -------
|
| bytes
|
| 128-bit hash
|
| """
|
| h = ToySHA520(self.rounds)
|
| if data:
|
| h.update(data)
|
| else:
|
| h._h = list(self._h)
|
| h._buffer = self._buffer
|
| h._counter = self._counter
|
| return h.finalize()
|
|
|
| def hexdigest(self, data: bytes = b'') -> str:
|
| """Hex digest."""
|
| return self.digest(data).hex()
|
|
|
|
|
| def build_toy_grover_circuit(
|
| toy: ToySHA520,
|
| target_hash: bytes,
|
| iterations: int = 10,
|
| ) -> Dict[str, Any]:
|
| """Build Grover circuit description for Toy SHA-520.
|
|
|
| This is a symbolic representation (not executable circuit code).
|
|
|
| Parameters
|
| ----------
|
| toy : ToySHA520
|
| Toy hash instance
|
| target_hash : bytes
|
| Target 16-byte hash
|
| iterations : int
|
| Number of Grover iterations
|
|
|
| Returns
|
| -------
|
| dict
|
| Circuit specification with gates and resources
|
| """
|
|
|
|
|
|
|
| n_qubits = 32
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| oracle_gates = 4 * 50 + 100
|
| oracle_depth = 50
|
|
|
|
|
| diffusion_gates = 4 * n_qubits + 50
|
| diffusion_depth = 30
|
|
|
|
|
| total_gates = iterations * (oracle_gates + diffusion_gates) + n_qubits
|
| total_depth = iterations * (oracle_depth + diffusion_depth) + n_qubits
|
|
|
|
|
| circuit_spec = {
|
| "algorithm": "Grover",
|
| "hash_variant": "Toy-SHA-520",
|
| "target_bits": n_qubits,
|
| "search_space": 2 ** n_qubits,
|
| "target_hash": target_hash.hex(),
|
| "grover_iterations": iterations,
|
| "n_qubits": n_qubits,
|
| "oracle_gates": oracle_gates,
|
| "oracle_depth": oracle_depth,
|
| "diffusion_gates": diffusion_gates,
|
| "diffusion_depth": diffusion_depth,
|
| "total_gates": total_gates,
|
| "total_depth": total_depth,
|
| "operations": [
|
| "Initialize superposition (H on all qubits)",
|
| f"Repeat {iterations} times:",
|
| " - Apply SHA-520 oracle (mark target hash)",
|
| " - Apply Grover diffusion operator",
|
| "Measure qubits",
|
| ],
|
| }
|
|
|
| return circuit_spec
|
|
|
|
|
| def estimate_toy_grover_speedup(target_bits: int = 32) -> Dict[str, Any]:
|
| """Estimate speedup of Grover over classical for toy SHA-520.
|
|
|
| Parameters
|
| ----------
|
| target_bits : int
|
| Bits in search space
|
|
|
| Returns
|
| -------
|
| dict
|
| Speedup metrics
|
| """
|
| search_space = 2 ** target_bits
|
|
|
|
|
| classical_evals = search_space
|
| classical_time_sec = classical_evals * 1e-6
|
|
|
|
|
| grover_iters = int((math.pi / 4.0) * math.sqrt(search_space))
|
|
|
|
|
| gates_per_iter = 400
|
| gate_time_sec = 100e-9
|
| grover_time_sec = grover_iters * gates_per_iter * gate_time_sec
|
|
|
| speedup = classical_time_sec / max(grover_time_sec, 1e-9)
|
|
|
| return {
|
| "target_bits": target_bits,
|
| "search_space": search_space,
|
| "classical_evaluations": classical_evals,
|
| "classical_time_sec": classical_time_sec,
|
| "grover_iterations": grover_iters,
|
| "gates_per_iteration": gates_per_iter,
|
| "gate_time_sec": gate_time_sec,
|
| "grover_time_sec": grover_time_sec,
|
| "speedup_factor": speedup,
|
| }
|
|
|
|
|
| if __name__ == "__main__":
|
| print("Toy SHA-520 for Cryptanalysis Testing")
|
| print("=" * 60)
|
|
|
|
|
| toy = ToySHA520(rounds=4)
|
| print(f"Toy SHA-520-{toy.rounds}")
|
| print(f" Word size: {toy.word_size} bits")
|
| print(f" State words: {toy.n_words}")
|
| print(f" Digest size: {toy.digest_size} bytes ({toy.digest_size * 8} bits)")
|
|
|
|
|
| print("\nTest vectors:")
|
| test_cases = [b'', b'abc', b'hello world', b'a' * 100]
|
|
|
| for msg in test_cases:
|
| digest = toy.digest(msg)
|
| msg_display = msg.decode() if len(msg) < 20 else f"{msg[:20].decode()}..."
|
| print(f" {msg_display:30s} -> {digest.hex()}")
|
|
|
|
|
| print("\n" + "=" * 60)
|
| print("Grover Circuit for Toy SHA-520")
|
|
|
| target = b'\x00' * 16
|
| circuit_spec = build_toy_grover_circuit(toy, target, iterations=10)
|
|
|
| print(f"\nCircuit specification:")
|
| for key, value in circuit_spec.items():
|
| if key != "operations":
|
| print(f" {key}: {value}")
|
|
|
| print(f"\nOperations:")
|
| for op in circuit_spec["operations"]:
|
| print(f" {op}")
|
|
|
|
|
| print("\n" + "=" * 60)
|
| print("Grover vs Classical Speedup")
|
|
|
| for bits in [16, 24, 32]:
|
| speedup = estimate_toy_grover_speedup(bits)
|
| print(
|
| f"\n{bits}-bit search:"
|
| f"\n Classical time: {speedup['classical_time_sec']:.2e} sec"
|
| f"\n Grover time: {speedup['grover_time_sec']:.2e} sec"
|
| f"\n Speedup: {speedup['speedup_factor']:.2e}x"
|
| )
|
|
|