Skip to content

digest: add optional impl: section to buffer_ct_variable! - #2477

Open
MavenRain wants to merge 2 commits into
RustCrypto:masterfrom
MavenRain:747-ct-variable-customized-init
Open

digest: add optional impl: section to buffer_ct_variable!#2477
MavenRain wants to merge 2 commits into
RustCrypto:masterfrom
MavenRain:747-ct-variable-customized-init

Conversation

@MavenRain

Copy link
Copy Markdown

Summary

Adds an optional trailing impl: ...; section to buffer_ct_variable!, with CustomizedInit as its first (currently only) supported trait.

This is the digest half of RustCrypto/hashes#747. The blake2 half is a small follow-up, described at the bottom.

Problem

buffer_fixed! and buffer_xof! both take an impl: trait list and both already carry a CustomizedInit arm. buffer_ct_variable! takes no such section, so a crate whose wrapper it generates has to hand-write the impl. blake2 does exactly that, twice, each time under a // TODO: impl in the macro comment:

// TODO: impl in the macro
impl<OutSize> CustomizedInit for Blake2b<OutSize>
where
    OutSize: ArraySize + IsLessOrEqual<U64, Output = True>,
{
    fn new_customized(customization: &[u8]) -> Self {
        Self {
            core: CustomizedInit::new_customized(customization),
            buffer: Default::default(),
        }
    }
}

Fix

Two new public arms accept the extra section and forward to an impl_inner: muncher, in the same shape buffer_fixed! uses (a per-trait arm that peels one ident and tail-recurses, plus a terminator). The generated impl carries the same $out_size: ArraySize + IsLessOrEqual<$max_size, Output = True> bound as every other impl the macro emits.

Both pre-existing call forms are unaffected: the section is optional and the arms are purely additive, so existing callers match byte for byte. groestl and kupyna, the only other buffer_ct_variable! callers I could find, compile unchanged against this branch.

One deliberate difference from blake2's hand-written impls: the generated new_customized is #[inline], matching fixed.rs and xof.rs.

Testing

New digest/tests/dummy_ct_variable.rs, modelled on the existing dummy_fixed.rs. It defines dummy variable-output cores (one customizable, one not), covers all four section combinations (exclude: SerializableState; present or absent, crossed with impl: present or absent), asserts absolute output values, and checks that the new section composes with the delegating max_size:-only arm through a SerializableState round trip.

Gates, all green: cargo build/test -p digest --all-features, cargo fmt --check, cargo clippy --all-features -- -D warnings, cargo doc --all-features --no-deps, and cargo +1.85.0 check -p digest --all-features --tests for MSRV.

Confirmed by mutation rather than by assertion count:

  • making the generated new_customized drop its argument fails both new tests
  • deleting either new public arm is a compile error in the test file, so the arms are not redundant with each other
  • removing impl: CustomizedInit; from a call site makes new_customized disappear with E0599, so the opt-in is real and nothing is emitted implicitly

End to end against real blake2, with a local [patch.crates-io] pointing digest at this branch: deleting both hand-written impls and passing impl: CustomizedInit; keeps the existing blake2b_persona KAT byte-identical, with persona.rs unchanged between the two runs. Keeping the hand-written impl and the section gives E0119 naming this macro, which is the positive proof that the section really emits an impl for the concrete Blake2b<OutSize> rather than silently expanding to nothing.

Questions for the maintainer

  1. CustomizedInit or TryCustomizedInit? Given digest: add TryCustomizedInit trait #2395 and ascon-xof128: replace implementation of CustomizedInit with TryCustomizedInit hashes#859 you may prefer the fallible trait here. I emitted the infallible one because the blanket impl<T: CustomizedInit> TryCustomizedInit for T makes emitting both a coherence error, and because there is no fallible counterpart to VariableOutputCoreCustomized for CtOutWrapper to delegate to. Happy to switch, or to add a second TryCustomizedInit ident to the vocabulary, if you'd rather add that core trait first.
  2. Overlap with Implement ZeroizeOnDrop for the output of buffer_ct_variable #2353. That PR adds ZeroizeOnDrop to this same macro and appends in the same tail region. If it lands first, ZeroizeOnDrop would be a natural second entry in this impl: vocabulary instead of a standalone impl. I'm happy to rebase in either order, or to rework this on top of it.
  3. Should the trait vocabulary be pre-populated with the rest of what the macro already emits unconditionally, so the section can also be used to narrow the generated set? I kept the scope minimal on purpose.

Follow-up

RustCrypto/hashes#747 then becomes: pass impl: CustomizedInit; to the two digest::buffer_ct_variable! invocations, delete the two hand-written impls (26 lines), and drop the now-unused CustomizedInit import. That change is ready and verified locally; I'll open it once this lands and is released, since it needs digest = "0.11.4".

Drafted with AI assistance; the design, the verification above, and this description were reviewed and re-run by me.

`buffer_fixed!` and `buffer_xof!` both accept an `impl:` trait list and both
already have a `CustomizedInit` arm, but `buffer_ct_variable!` has no such
section. Downstream crates whose wrapper is generated by it therefore have to
hand-write the impl, which is what blake2 does today behind two
`// TODO: impl in the macro` comments (RustCrypto/hashes#747).

Add an optional trailing `impl: ...;` section served by two new public arms
which forward to an `impl_inner:` muncher, mirroring how `buffer_fixed!`
already works. `CustomizedInit` is the only trait in the vocabulary for now;
it requires the "core" type to implement `VariableOutputCoreCustomized`.

The section is optional and the new arms are additive, so both pre-existing
call forms keep matching byte for byte.

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant