# Report: RIPEMD-160 collision truncated to 48 bits (λ=24)

## Answer

```json
{"algo":"ripemd160","lambda":24,"inputA":"imd-22739628","inputB":"imd-33541843"}
```

Inputs are UTF-8 strings (12 bytes each), hashed as their raw bytes with no trailing newline.

## Evidence (facts — recomputed locally on 2026-10-06)

| Input | Full RIPEMD-160 digest |
|---|---|
| `imd-22739628` | `3318dbb891e7`348a4a38e96570bb418d9a5d70da |
| `imd-33541843` | `3318dbb891e7`c6f83d3adea4e0221a7ac64abcc3 |

- The first 48 bits (6 bytes) are identical: `0x3318dbb891e7`.
- The inputs are distinct; the full 160-bit digests differ after byte 6.
- Two independent implementations agree on both digests: Python `hashlib.new("ripemd160")`
  (OpenSSL-backed) and the `openssl dgst -ripemd160` CLI.
- Sanity check of the implementation: RIPEMD-160 of the empty string returned
  `9c1185a5c5e9fc54612808977ee8f548b2258d31`, matching the published test vector in the
  RIPEMD-160 specification (Dobbertin, Bosselaers, Preneel; https://homes.esat.kuleuven.be/~bosselae/ripemd160.html).

Reproduce:

```sh
printf 'imd-22739628' | openssl dgst -ripemd160
printf 'imd-33541843' | openssl dgst -ripemd160
```

## Method

`tools/find_collision.py` hashes the sequence `imd-0`, `imd-1`, … and keeps a dictionary from
the 6-byte truncated digest to the counter. The first repeated truncated digest gives the pair.
The search finished after 33,541,844 evaluations (~2^25.0) in about 170 s of CPU time.

## Inference

The expected number of evaluations for a first collision on an n-bit output is about
√(π/2 · 2^n) ≈ 1.25 · 2^24 ≈ 2.1 × 10^7 for n = 48 (standard birthday bound). The observed
3.35 × 10^7 is ~1.6× that expectation, which is within normal variance of a single run
(the stopping time has a wide, Rayleigh-like distribution). This is consistent with treating the
truncated function as random; it is not evidence of any structural weakness.

## Uncertainty / unanswered

- The verifier's exact encoding rule (how it decides "hex 0x…" vs "utf8") was not observed;
  the inputs were chosen without a `0x` prefix so the UTF-8 interpretation is the only
  reasonable reading. If a verifier hashed a trailing newline or another encoding, the result would not hold.
- "Truncated to 48 bits MSB" was interpreted as the first 6 bytes of the standard big-endian
  hex digest rendering. Under that interpretation the collision is verified; an alternative
  bit-order convention was not tested.
- No independent reviewer has checked this report; the checks above are self-run.
