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

## Answer

`collision.json`:

```json
{"algo":"ripemd160","lambda":24,"inputA":"rmd48-12932577","inputB":"rmd48-13878042"}
```

Both inputs are UTF-8 strings (plain ASCII, no `0x` prefix, so neither reads as hex).

| Input | RIPEMD-160 (full digest) | First 48 bits |
|---|---|---|
| `rmd48-12932577` | `f38b48a5fbb5fa7dfd4be58b45df9cb55f1c37df` | `f38b48a5fbb5` |
| `rmd48-13878042` | `f38b48a5fbb5350b30a46c5e462087d3b120d27a` | `f38b48a5fbb5` |

The two inputs are different strings, and the first 6 bytes (48 bits, read from the most significant end) of their digests are the same.

## Evidence (facts, reproduced locally)

- **How it was found:** `scripts/find_collision.mjs` hashes `rmd48-0`, `rmd48-1`, … with Node's `crypto.createHash("ripemd160")` (backed by OpenSSL). It stores each 48-bit prefix in a hash map and stops at the first repeated prefix. It hit a match after **13,878,043 evaluations**, about 2^23.7, in about 39 s.
- **Independent check 1:** Python `hashlib.new("ripemd160")` gives the same full digests shown above. With `collision.json` parsed as JSON, `inputA != inputB` is True and `digest[:6]` matches.
- **Independent check 2:** `openssl dgst -ripemd160` gives the same two digests.
- **Implementation sanity check:** Node and Python both give `8eb208f7e05d987a9b044a8e98c6b087f15a0bfc` for `"abc"`. This is the RIPEMD-160 test vector published by the algorithm's authors (Dobbertin, Bosselaers, Preneel, *RIPEMD-160: A Strengthened Version of RIPEMD*, 1996; test vectors at https://homes.esat.kuleuven.be/~bosselae/ripemd160.html).

## Inferences

- The work matches the birthday bound. With N = 2^48 possible prefixes, the expected number of tries before the first collision is about √(πN/2) ≈ 2^24.3 ≈ 21 million. Our 13.9 million tries is within normal variation. No weakness in RIPEMD-160 was used or is implied. This is a generic attack that works on any 48-bit truncation.
- The task's phrase "48 bits MSB" is taken to mean the first 6 bytes of the standard big-endian hex digest. The task states 48 bits = 2λ explicitly, so the length is not in doubt.

## Uncertainty / unanswered questions

- I did not run the verifier's own "SIMD" implementation. I am assuming it hashes the UTF-8 bytes of a string that does not start with `0x`, as the task's format describes. If it treated the strings any other way, the result would not hold. That is the only open risk.
- No web sources were needed beyond the test-vector reference above. All other claims can be reproduced by running the script and the commands listed here.
