internet 0.0.4

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

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

/// A cipher suites following [Section A.5].
///
/// [Section A.5]: https://datatracker.ietf.org/doc/html/rfc2246#appendix-A.5
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u16)]
pub enum CipherSuite {
    /// TLS_NULL_WITH_NULL_NULL
    TlsNullWithNullNull = 0x0000,
    /// TLS_RSA_WITH_NULL_MD5
    TlsRsaWithNullMd5 = 0x0001,
    /// TLS_RSA_WITH_NULL_SHA
    TlsRsaWithNullSha = 0x0002,
    /// TLS_RSA_EXPORT_WITH_RC4_40_MD5
    TlsRsaExportWithRc440Md5 = 0x0003,
    /// TLS_RSA_WITH_RC4_128_MD5
    TlsRsaWithRc4128Md5 = 0x0004,
    /// TLS_RSA_WITH_RC4_128_SHA
    TlsRsaWithRc4128Sha = 0x0005,
    /// TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5
    TlsRsaExportWithRc2Cbc40Md5 = 0x0006,
    /// TLS_RSA_WITH_IDEA_CBC_SHA
    TlsRsaWithIdeaCbcSha = 0x0007,
    /// TLS_RSA_EXPORT_WITH_DES40_CBC_SHA
    TlsRsaExportWithDes40CbcSha = 0x0008,
    /// TLS_RSA_WITH_DES_CBC_SHA
    TlsRsaWithDesCbcSha = 0x0009,
    /// TLS_RSA_WITH_3DES_EDE_CBC_SHA
    TlsRsaWith3desEdeCbcSha = 0x000A,
    /// TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA
    TlsDhDssExportWithDes40CbcSha = 0x000B,
    /// TLS_DH_DSS_WITH_DES_CBC_SHA
    TlsDhDssWithDesCbcSha = 0x000C,
    /// TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
    TlsDhDssWith3desEdeCbcSha = 0x000D,
    /// TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA
    TlsDhRsaExportWithDes40CbcSha = 0x000E,
    /// TLS_DH_RSA_WITH_DES_CBC_SHA
    TlsDhRsaWithDesCbcSha = 0x000F,
    /// TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
    TlsDhRsaWith3desEdeCbcSha = 0x0010,
    /// TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
    TlsDheDssExportWithDes40CbcSha = 0x0011,
    /// TLS_DHE_DSS_WITH_DES_CBC_SHA
    TlsDheDssWithDesCbcSha = 0x0012,
    /// TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
    TlsDheDssWith3desEdeCbcSha = 0x0013,
    /// TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
    TlsDheRsaExportWithDes40CbcSha = 0x0014,
    /// TLS_DHE_RSA_WITH_DES_CBC_SHA
    TlsDheRsaWithDesCbcSha = 0x0015,
    /// TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
    TlsDheRsaWith3desEdeCbcSha = 0x0016,
    /// TLS_DH_ANON_EXPORT_WITH_RC4_40_MD5
    TlsDhAnonExportWithRc440Md5 = 0x0017,
    /// TLS_DH_ANON_WITH_RC4_128_MD5
    TlsDhAnonWithRc4128Md5 = 0x0018,
    /// TLS_DH_ANON_EXPORT_WITH_DES40_CBC_SHA
    TlsDhAnonExportWithDes40CbcSha = 0x0019,
    /// TLS_DH_ANON_WITH_DES_CBC_SHA
    TlsDhAnonWithDesCbcSha = 0x001A,
    /// TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA
    TlsDhAnonWith3desEdeCbcSha = 0x001B,
}

impl Codec for CipherSuite {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        let bytes = (*self as u16).to_be_bytes();
        bytes.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        let bytes = <[u8; 2]>::decode(reader, ())?;
        let val = u16::from_be_bytes(bytes);
        Self::try_from(val).map_err(|_| BufError::UnexpectedValue)
    }
}

impl TryFrom<u16> for CipherSuite {
    type Error = ();

    fn try_from(value: u16) -> Result<Self, Self::Error> {
        match value {
            0x0000 => Ok(Self::TlsNullWithNullNull),
            0x0001 => Ok(Self::TlsRsaWithNullMd5),
            0x0002 => Ok(Self::TlsRsaWithNullSha),
            0x0003 => Ok(Self::TlsRsaExportWithRc440Md5),
            0x0004 => Ok(Self::TlsRsaWithRc4128Md5),
            0x0005 => Ok(Self::TlsRsaWithRc4128Sha),
            0x0006 => Ok(Self::TlsRsaExportWithRc2Cbc40Md5),
            0x0007 => Ok(Self::TlsRsaWithIdeaCbcSha),
            0x0008 => Ok(Self::TlsRsaExportWithDes40CbcSha),
            0x0009 => Ok(Self::TlsRsaWithDesCbcSha),
            0x000A => Ok(Self::TlsRsaWith3desEdeCbcSha),
            0x000B => Ok(Self::TlsDhDssExportWithDes40CbcSha),
            0x000C => Ok(Self::TlsDhDssWithDesCbcSha),
            0x000D => Ok(Self::TlsDhDssWith3desEdeCbcSha),
            0x000E => Ok(Self::TlsDhRsaExportWithDes40CbcSha),
            0x000F => Ok(Self::TlsDhRsaWithDesCbcSha),
            0x0010 => Ok(Self::TlsDhRsaWith3desEdeCbcSha),
            0x0011 => Ok(Self::TlsDheDssExportWithDes40CbcSha),
            0x0012 => Ok(Self::TlsDheDssWithDesCbcSha),
            0x0013 => Ok(Self::TlsDheDssWith3desEdeCbcSha),
            0x0014 => Ok(Self::TlsDheRsaExportWithDes40CbcSha),
            0x0015 => Ok(Self::TlsDheRsaWithDesCbcSha),
            0x0016 => Ok(Self::TlsDheRsaWith3desEdeCbcSha),
            0x0017 => Ok(Self::TlsDhAnonExportWithRc440Md5),
            0x0018 => Ok(Self::TlsDhAnonWithRc4128Md5),
            0x0019 => Ok(Self::TlsDhAnonExportWithDes40CbcSha),
            0x001A => Ok(Self::TlsDhAnonWithDesCbcSha),
            0x001B => Ok(Self::TlsDhAnonWith3desEdeCbcSha),
            _ => Err(()),
        }
    }
}

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

    use super::CipherSuite;
    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 cipher_suite() {
        let etalon_bytes = &[0x00, 0x0A];
        let etalon_struct = CipherSuite::TlsRsaWith3desEdeCbcSha;

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