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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions