traits icon indicating copy to clipboard operation
traits copied to clipboard

crypto-common: add SerializableState trait

Open rpiasetskyi opened this issue 3 years ago • 12 comments

This trait is used for saving internal state of the hash core and restoring it later.

The original issue was discussed here: https://github.com/RustCrypto/hashes/issues/310

rpiasetskyi avatar Aug 10 '22 21:08 rpiasetskyi

I think the core notion here is a "serializable state" rather than an "internal state"

tarcieri avatar Aug 10 '22 22:08 tarcieri

Sorry, missed the last comment. I will move the trait to crypto-common and use serialize/deserialize methods.

Should deserialize method return Result (if the buffer contains invalid values)?

rpiasetskyi avatar Aug 19 '22 12:08 rpiasetskyi

The versions of the crates have to be updated: should they be 0.1.7/0.10.4 or 0.2.0/0.11.0?

Right now crypto-common is 0.1.6 and digest is 0.10.3.

rpiasetskyi avatar Aug 19 '22 15:08 rpiasetskyi

Do not forget to also implement it for CoreWrapper and ideally for RtVariableCoreWrapper.

There is a small issue with RtVariableCoreWrapper: it has a member that has usize type.

It is needed for SerializedStateSize definition. It should be something like:

type SerializedStateSize =  Sum<Sum<Add1<T::SerializedStateSize>, UsizeSize>, T::BlockSize>;

Is there any known UsizeSize definition?

Or should it be implemented via cfg?

#[cfg(target_pointer_width = "16")]
type UsizeSize = U2;
#[cfg(target_pointer_width = "32")]
type UsizeSize = U4;
#[cfg(target_pointer_width = "64")]
type UsizeSize = U8;

What should be the best place to put it In this case?

rpiasetskyi avatar Aug 31 '22 15:08 rpiasetskyi

The serialized state needs to be consistent regardless of the target pointer width.

You should pick a sensible on-the-wire representation (u32 or u64) and use a try_from conversion when deserializing it.

tarcieri avatar Aug 31 '22 15:08 tarcieri

I think we can simply transform output_size to u8 using TryInto and unwrap. I am not aware about variable hashes which support output size bigger than 255 bytes (XOF does not count). In future versions it could be worth to replace usize with u8.

newpavlov avatar Aug 31 '22 21:08 newpavlov

Converted to draft to avoid emails about failed tests.

UPD: Did not help :(

rpiasetskyi avatar Sep 03 '22 20:09 rpiasetskyi

Does it make sense to implement SerializableState for some commonly used types?

impl SerializableState for u8 {
    type SerializedStateSize = U1;

    fn serialize(&self) -> SerializedState<Self> {
        [*self].into()
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        Ok(serialized_state[0])
    }
}

impl SerializableState for u64 {
    type SerializedStateSize = U8;

    fn serialize(&self) -> SerializedState<Self> {
        self.to_le_bytes().into()
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        Ok(u64::from_le_bytes((*serialized_state).into()))
    }
}

impl SerializableState for [u64; 16] {
    type SerializedStateSize = U128;

    fn serialize(&self) -> SerializedState<Self> {
        let mut serialized_state = SerializedState::<Self>::default();
        for (val, chunk) in self.iter().zip(serialized_state.chunks_exact_mut(8)) {
            chunk.copy_from_slice(&val.to_le_bytes());
        }

        serialized_state
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        let mut array = Self::default();

        for (val, chunk) in array.iter_mut().zip(serialized_state.chunks_exact(8)) {
            *val = u64::from_le_bytes(chunk.try_into().unwrap());
        }

        Ok(array)
    }
}

It can be implemented in a similar way to this https://github.com/fizyk20/generic-array/blob/master/src/impls.rs#L183.

This can move the for loop (array serialization/deserialization) in one place and make the code a little bit simpler.

type State = [u64; 16];

impl SerializableState for GroestlLongVarCore {
    type SerializedStateSize = Sum<
        <u64 as SerializableState>::SerializedStateSize,
        <State as SerializableState>::SerializedStateSize,
    >;

    fn serialize(&self) -> SerializedState<Self> {
        self.state.serialize().concat(self.blocks_len.serialize())
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        let (serialized_state, serialized_blocks_len) =
            Split::<_, <State as SerializableState>::SerializedStateSize>::split(serialized_state);

        Ok(Self {
            state: State::deserialize(serialized_state)?,
            blocks_len: u64::deserialize(serialized_blocks_len)?,
        })
    }
}

And it might be possible to implement #[derive(SerializableState)] for types that do not need to check anything.

rpiasetskyi avatar Sep 14 '22 15:09 rpiasetskyi

With the introduction of the AssociatedOid trait and new generic parameter for CtVariableCoreWrapper, is it needed to add it to serialized state?

rpiasetskyi avatar Sep 16 '22 11:09 rpiasetskyi

No, those are type-level properties which are identical regardless of the state.

tarcieri avatar Sep 16 '22 13:09 tarcieri

Does it make sense to implement SerializableState for some commonly used types?

impl SerializableState for u8 {
    type SerializedStateSize = U1;

    fn serialize(&self) -> SerializedState<Self> {
        [*self].into()
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        Ok(serialized_state[0])
    }
}

impl SerializableState for u64 {
    type SerializedStateSize = U8;

    fn serialize(&self) -> SerializedState<Self> {
        self.to_le_bytes().into()
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        Ok(u64::from_le_bytes((*serialized_state).into()))
    }
}

impl SerializableState for [u64; 16] {
    type SerializedStateSize = U128;

    fn serialize(&self) -> SerializedState<Self> {
        let mut serialized_state = SerializedState::<Self>::default();
        for (val, chunk) in self.iter().zip(serialized_state.chunks_exact_mut(8)) {
            chunk.copy_from_slice(&val.to_le_bytes());
        }

        serialized_state
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        let mut array = Self::default();

        for (val, chunk) in array.iter_mut().zip(serialized_state.chunks_exact(8)) {
            *val = u64::from_le_bytes(chunk.try_into().unwrap());
        }

        Ok(array)
    }
}

It can be implemented in a similar way to this https://github.com/fizyk20/generic-array/blob/master/src/impls.rs#L183.

This can move the for loop (array serialization/deserialization) in one place and make the code a little bit simpler.

type State = [u64; 16];

impl SerializableState for GroestlLongVarCore {
    type SerializedStateSize = Sum<
        <u64 as SerializableState>::SerializedStateSize,
        <State as SerializableState>::SerializedStateSize,
    >;

    fn serialize(&self) -> SerializedState<Self> {
        self.state.serialize().concat(self.blocks_len.serialize())
    }

    fn deserialize(
        serialized_state: &SerializedState<Self>,
    ) -> Result<Self, DeserializeStateError> {
        let (serialized_state, serialized_blocks_len) =
            Split::<_, <State as SerializableState>::SerializedStateSize>::split(serialized_state);

        Ok(Self {
            state: State::deserialize(serialized_state)?,
            blocks_len: u64::deserialize(serialized_blocks_len)?,
        })
    }
}

And it might be possible to implement #[derive(SerializableState)] for types that do not need to check anything.

What do you think about https://github.com/RustCrypto/traits/pull/1078/commits/3447449814c3ee2bae77834bd23d1984895603fa? If this is okay, I will also add #[derive(SerializableState)].

rpiasetskyi avatar Sep 18 '22 20:09 rpiasetskyi

Could you please have a look at https://github.com/RustCrypto/traits/pull/1078/commits/afc029e5f43167d3787e5b5300e9a3961d77a256 ? This is the implementation of #[derive(SerializableState)] macro. It makes it very easy to implement SerializableState trait for cores (e.g. for fsb https://github.com/RustCrypto/hashes/pull/385/commits/589b43bb6009fadb34f13b867d9d06cdd288a3f5).

The current implementation doesn't support generics for now.

Also, crypto_common should be imported in the file that makes derivation.

rpiasetskyi avatar Sep 26 '22 11:09 rpiasetskyi

:wave: any chance of getting this merged any time in the near future?

waynr avatar Oct 10 '23 19:10 waynr

@waynr it needs a rebase, at the very least

tarcieri avatar Oct 10 '23 20:10 tarcieri

@tarcieri I'm attempting to rebase but running into compile errors that are pretty much totally incomprehensible to me. This repo is like a firehose of generic types and my puny little brain only has capacity for a trickle.

waynr avatar Oct 11 '23 03:10 waynr

I think @rpiasetskyi said he would take a look

tarcieri avatar Oct 11 '23 14:10 tarcieri

I figured out that the problem had to do with updating the crypto-common dependency in the digest crate and opened a PR to show the same problem on master branch: https://github.com/RustCrypto/traits/pull/1359

waynr avatar Oct 11 '23 16:10 waynr

Just a quick note - it looks like the issue of the local dependency on crypto-common being broken in master branch for the digest crate is fixed in #1358 so this PR might need to wait for that before being rebased.

waynr avatar Oct 11 '23 17:10 waynr

I am doing a rebase (and migration to hybrid_array) using https://github.com/RustCrypto/traits/pull/1358.

rpiasetskyi avatar Oct 12 '23 15:10 rpiasetskyi

@tarcieri Sorry, I was expecting it to update the PR but not to merge it :(

rpiasetskyi avatar Oct 16 '23 20:10 rpiasetskyi

Reopen.

rpiasetskyi avatar Oct 16 '23 20:10 rpiasetskyi

I think you pushed 0 commits which auto-closed the PR? Regardless I can't reopen it... I think you'd need to push some new commits

tarcieri avatar Oct 16 '23 20:10 tarcieri