internet 0.1.0

Network library for rust
Documentation
//! Change Cipher Spec encoding following [RFC 2246].
//!
//! Encoding is supported for the following structures:
//!
//!  - [`ChangeCipherSpecType`]
//!  - [`ChangeCipherSpec`]
//!
//! [RFC 2246]: https://datatracker.ietf.org/doc/html/rfc2246

use crate::{
    Buf,
    BufError::{self},
    BufMut, BufResult, Codec, Cursor,
};

/// A ChangeCipherSpec type following [Section 7.1].
///
/// [Section 7.1]: https://datatracker.ietf.org/doc/html/rfc2246#section-7.1
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum ChangeCipherSpecType {
    /// change_cipher_spec(1)
    ChangeCipherSpec = 1,
}

impl Codec for ChangeCipherSpecType {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        (*self as u8).encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        match u8::decode(reader, ())? {
            x if x == (Self::ChangeCipherSpec as u8) => Ok(Self::ChangeCipherSpec),
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

/// A ChangeCipherSpec following [Section 7.1].
///
/// [Section 7.1]: https://datatracker.ietf.org/doc/html/rfc2246#section-7.1
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ChangeCipherSpec {
    /// The type of the change cipher spec.
    pub type_: ChangeCipherSpecType,
}

impl Codec for ChangeCipherSpec {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.type_.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        Ok(Self {
            type_: ChangeCipherSpecType::decode(reader, ())?,
        })
    }
}

#[cfg(test)]
mod tests {
    use core::fmt::Debug;

    use super::{ChangeCipherSpec, ChangeCipherSpecType};
    use crate::{Codec, Cursor};

    fn codec_roundtrip<T: Codec<C> + Debug + Eq, C: Copy>(
        etalon_struct: T,
        etalon_bytes: &[u8],
        context: C,
    ) {
        let mut encoded_bytes = vec![];
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            etalon_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);

        let decoded_struct = {
            let reader = &mut Cursor::new(&mut encoded_bytes);
            T::decode(reader, context).unwrap()
        };
        assert_eq!(etalon_struct, decoded_struct);

        encoded_bytes.fill(0x00);
        {
            let writer = &mut Cursor::new(&mut encoded_bytes);
            decoded_struct.encode(writer, context).unwrap();
        }
        assert_eq!(etalon_bytes, &encoded_bytes);
    }

    #[test]
    fn change_cipher_spec() {
        let etalon_bytes = &[0x01];
        let etalon_struct = ChangeCipherSpec {
            type_: ChangeCipherSpecType::ChangeCipherSpec,
        };

        codec_roundtrip(etalon_struct, etalon_bytes, ());
    }
}