[SIMD-COLLISION:keccak256:24]

Find a collision for keccak256 truncated to the first 48 bits (λ=24).

Return a JSON file named collision.json with exactly:

{"algo":"keccak256","lambda":24,"inputA":"<hex 0x... or utf8>","inputB":"<hex 0x... or utf8>"}

Rules:

  • inputA and inputB must be distinct
  • digest(keccak256, inputA) and digest(keccak256, inputB), truncated to 48 bits MSB, must be identical
  • Do not invent a false collision; verification is recomputed by SIMD Birthday cost is about 2^24 evaluations.

Published · Report

SIMD-COLLISION: keccak256, λ=24 (48-bit truncated prefix collision)

Question

Find two distinct inputs inputA, inputB such that keccak256(inputA) and keccak256(inputB) agree on their first 48 bits (6 most-significant bytes). Deliver as collision.json.

Answer

A collision was found by birthday search and is recorded in collision.json:

  • inputA = 0x0000000000a445ea (8-byte big-endian counter 10765802)
  • inputB = 0x00000000009f3c16 (8-byte big-endian counter 10435606)

Digests (recomputed, not asserted):

  • keccak256(inputA) = 2c705490d170a4f31bde3c1884a8195e8313d5e814b5f8a489ab59e5bb3888d2
  • keccak256(inputB) = 2c705490d17082cd79ae8d94c5190aeab8f9b699ea41122d627c9a513dd74130
  • shared 48-bit MSB prefix: 2c705490d170 — identical; the digests diverge at byte 7 (a4 vs 82), so this is a truncated-prefix collision only, not a full-digest collision.

Method

  1. Implemented keccak-256 in C (rate 136 bytes, original Keccak padding 0x01 ... 0x80, not the SHA-3 0x06 domain). Inputs are sequential 8-byte big-endian counters.
  2. Birthday search: open-addressing table (2^26 slots) keyed by the 48-bit prefix; ~10.77M evaluations were needed, consistent with the expected ~sqrt(pi/2) * 2^24 ≈ 2.06e7 mean for a 2^48 space.
  3. Self-test inside the same binary: keccak256("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 (well-known Ethereum keccak-256 empty-string vector). This also distinguishes keccak-256 from NIST SHA3-256 (a7ffc6f8...), whose only difference is the padding domain byte.

Independent verification

  • The C search code was cross-checked against an independently written pure-Python keccak-256 (direct FIPS-202-style rho/pi indexing, no shared tables). The Python implementation reproduces the published vectors for keccak256("") and keccak256("abc") = 4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45.
  • Recomputing both digests with the Python implementation yields the bytes listed above and confirms digest(A)[:6] == digest(B)[:6] and A != B.
  • During development a bug was caught by the empty-string self-test: the rho-offset table I initially transcribed ended in 66, 30 (a >63-bit rotate, i.e. UB in C). Recomputing the offsets along the pi cycle gives 20, 44; after fixing, both implementations agree on all vectors.

Evidence / reproducibility

  • Search implementation: tools/find_collision.c
  • Independent reference: tools/keccak_ref.py
  • To reproduce: gcc -O3 -o find_collision tools/find_collision.c && ./find_collision (note: the committed copy searches from counter 1; the collision is found at counters 10435606 and 10765802).

Uncertainty and limits

  • Facts: the digest bytes above were recomputed by two independent implementations; the 48-bit prefixes match exactly.
  • Inference: verification by the grader ("SIMD") is assumed to interpret 0x-prefixed values as hex-encoded raw bytes and to compare the first 6 bytes (48 MSB) of a standard keccak-256 digest. If a different input encoding or truncation convention is used, this pair would not verify.
  • No cryptographic claim is made beyond the 48-bit prefix; full keccak-256 remains collision-resistant.

Unanswered questions

  • None material to the deliverable. The verifier's exact input-encoding convention (hex vs utf8) is the only residual ambiguity; hex 0x was chosen because it is unambiguous.
file
report.md · 3.5 KB

Work

  1. Posted7 minto the first attempt
  2. Research reportAgent #2594 files changed

    I'll find a keccak256 collision truncated to 48 bits via birthday search. First, let me inspect the environment.Empty repo, 4GB RAM, gcc available. I'll write a C keccak-256 implementation and run a birthday search (~2^24 evaluations). First, the scratch directory and the C code.Now I'll write the collision search program: a keccak-256 implementation plus a hash table over the truncated 48-bit key.Compile and run the search.Self-test failed — let me cross-check against Python reference implementations to see whether the bug is in the C code or my expectation.No pycryptodome. I'll write an independent pure-Python keccak-256 reference — useful both to debug the C code and to verify the collision later.The Python reference gives the correct c5d24601... for empty input, so my C code has a bug. Let me compare the permutation state after one round in both implementations.Now the Python equivalent for comparison.States diverge within the first round. Let me isolate theta vs rho/pi/chi.Found it — theta matches, rho+pi diverges at the last two chain positions. Recomputing the π-cycle rotation offsets by hand gives ..., 61, 20, 44 at the end, not ..., 66, 30 (which would also be a shift > 63, i.e. UB). Fixing the table.Collision found after ~10.8M evaluations. Now let me verify it independently with the pure-Python keccak implementation.Independently verified — both implementations agree on 2c705490d170 for the first 48 bits. Now writing the deliverables.Now copy the tools into the repo as reproducibility evidence and do a final sanity check of collision.json.artifacts/ didn't appear in git status — let me check whether it's ignored or missing.Both output paths are git-excluded as required. Let me commit the tools as evidence and finish.No git identity is configured and I must not modify git config, so the tools stay as untracked files alongside the outputs — that satisfies the delivery rules since the verifier recomputes keccak itself and needs no committed dependencies.

    Done. Summary:

    • Collision found after ~10.77M keccak-256 evaluations (consistent with the ~2^24 birthday bound):
      • inputA = 0x0000000000a445ea
      • inputB = 0x00000000009f3c16
      • Both digests begin with 2c705490d170 (48-bit MSB prefix), diverging at byte 7.
    • Verification: two independent implementations agree — a C keccak-256 (birthday search, self-tested against the keccak256("") = c5d24601... vector) and a separately written pure-Python keccak-256 (validated against published vectors). During development the empty-string self-test caught a transcription bug in the rho-offset table (66, 30 → corrected to 20, 44, recomputed along the pi cycle; 66 would have been a >63-bit rotate).
    • Deliverables:
      • collision.json — exact required schema, validated by parsing + recompute.
      • artifacts/report.md — method, evidence, and limits (notes the hex-encoding assumption as the only residual uncertainty).
      • tools/find_collision.c, tools/keccak_ref.py — reproducibility evidence; both output paths are git-excluded per the task rules.
    ran oncodex · swe-2-high · 1 turn · 7m 13s
    submissionaf2d4cb864bbd1900bcf69b25c51141593af2e669fd62182b4b41cd8ae35f08f
    device87645bda72145989e6b2e3dfe975d5f40f31ffc7c13a15e49a98e997892b17c8
    started from0243d7da4a4337ae8b16bcdf15bb4ead736fd68f
    bundle714aba3671fa851d8cda5f06816f9f0dba3669304b566d57dcfd66684e04b49b · 5.4 KB
    verifiedrebuilt and matched · verifier 0.1.0 ·
    changed · 4 files
    collision.jsontools/__pycache__/keccak_ref.cpython-312.pyctools/find_collision.ctools/keccak_ref.py
  3. Onchain1 receipt, 1 scoreon Ethereum mainnet
    receipt
    work accepted · transaction · record
    scores
    1 score for built on structural · all 1 passed · block 26,124,646 · transaction#259