Skip to content

Byte-injective transcript sponge adapter #419

@x-senpai-x

Description

@x-senpai-x

Summary

The Poseidon2 and Skyscraper duplex-sponge adapters decode each 32-byte lane via from_le_bytes_mod_order (or equivalent). This reduces lane values ≥ p mod p, so the bytes → Fr → bytes round-trip is only byte-injective when every absorbed lane is already < p.

All absorb sites write canonical encodings:

  • Field elements serialized via field_to_bytes_le (always < p by construction)
  • WHIR engine outputs (themselves canonical field elements)
  • ASCII domain-separator bytes (every byte < 0x80, so any 32-byte lane is trivially < p)

Why it's fragile

Nothing at the sponge layer enforces this invariant. spongefish::DuplexSponge::absorb takes &[u8] and writes raw bytes straight into permutation_state — the Permutation::permute impl has no way to reject non-canonical input.

Any of the following would silently break byte-injectivity:

  • A raw SHA-256 / Keccak-256 digest (~25% of outputs ≥ p)
  • 32 bytes from a randomness beacon or /dev/urandom
  • A non-ASCII DS that lands above 0x80 in the MSB of a lane

Affected files

Both sponges share the same adapter (utils::bytes_to_field / utils::field_to_bytes_le)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions