internet 0.1.0

Network library for rust
Documentation
//! TLS 1.0 Alert encoding following [RFC 2246].
//!
//! Encoding is supported for the following structures:
//!
//!  - [`AlertLevel`]
//!  - [`AlertDescription`]
//!  - [`Alert`]
//!
//! [RFC 2246]: https://datatracker.ietf.org/doc/html/rfc2246

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

/// An alert level following [Section 7.2].
///
/// [Section 7.2]: https://datatracker.ietf.org/doc/html/rfc2246#section-7.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum AlertLevel {
    /// warning(1)
    Warning = 1,
    /// fatal(2)
    Fatal = 2,
}

impl Codec for AlertLevel {
    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::Warning as u8) => Ok(Self::Warning),
            x if x == (Self::Fatal as u8) => Ok(Self::Fatal),
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

/// An alert description following [Section 7.2.2].
///
/// [Section 7.2.2]: https://datatracker.ietf.org/doc/html/rfc2246#section-7.2.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum AlertDescription {
    /// close_notify(0)
    CloseNotify = 0,
    /// unexpected_message(10)
    UnexpectedMessage = 10,
    /// bad_record_mac(20)
    BadRecordMac = 20,
    /// decryption_failed(21)
    DecryptionFailed = 21,
    /// record_overflow(22)
    RecordOverflow = 22,
    /// decompression_failure(30)
    DecompressionFailure = 30,
    /// handshake_failure(40)
    HandshakeFailure = 40,
    /// bad_certificate(42)
    BadCertificate = 42,
    /// unsupported_certificate(43)
    UnsupportedCertificate = 43,
    /// certificate_revoked(44)
    CertificateRevoked = 44,
    /// certificate_expired(45)
    CertificateExpired = 45,
    /// certificate_unknown(46)
    CertificateUnknown = 46,
    /// illegal_parameter(47)
    IllegalParameter = 47,
    /// unknown_ca(48)
    UnknownCa = 48,
    /// access_denied(49)
    AccessDenied = 49,
    /// decode_error(50)
    DecodeError = 50,
    /// decrypt_error(51)
    DecryptError = 51,
    /// export_restriction(60)
    ExportRestriction = 60,
    /// protocol_version(70)
    ProtocolVersion = 70,
    /// insufficient_security(71)
    InsufficientSecurity = 71,
    /// internal_error(80)
    InternalError = 80,
    /// user_canceled(90)
    UserCanceled = 90,
    /// no_renegotiation(100)
    NoRenegotiation = 100,
}

impl Codec for AlertDescription {
    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::CloseNotify as u8) => Ok(Self::CloseNotify),
            x if x == (Self::UnexpectedMessage as u8) => Ok(Self::UnexpectedMessage),
            x if x == (Self::BadRecordMac as u8) => Ok(Self::BadRecordMac),
            x if x == (Self::DecryptionFailed as u8) => Ok(Self::DecryptionFailed),
            x if x == (Self::RecordOverflow as u8) => Ok(Self::RecordOverflow),
            x if x == (Self::DecompressionFailure as u8) => Ok(Self::DecompressionFailure),
            x if x == (Self::HandshakeFailure as u8) => Ok(Self::HandshakeFailure),
            x if x == (Self::BadCertificate as u8) => Ok(Self::BadCertificate),
            x if x == (Self::UnsupportedCertificate as u8) => Ok(Self::UnsupportedCertificate),
            x if x == (Self::CertificateRevoked as u8) => Ok(Self::CertificateRevoked),
            x if x == (Self::CertificateExpired as u8) => Ok(Self::CertificateExpired),
            x if x == (Self::CertificateUnknown as u8) => Ok(Self::CertificateUnknown),
            x if x == (Self::IllegalParameter as u8) => Ok(Self::IllegalParameter),
            x if x == (Self::UnknownCa as u8) => Ok(Self::UnknownCa),
            x if x == (Self::AccessDenied as u8) => Ok(Self::AccessDenied),
            x if x == (Self::DecodeError as u8) => Ok(Self::DecodeError),
            x if x == (Self::DecryptError as u8) => Ok(Self::DecryptError),
            x if x == (Self::ExportRestriction as u8) => Ok(Self::ExportRestriction),
            x if x == (Self::ProtocolVersion as u8) => Ok(Self::ProtocolVersion),
            x if x == (Self::InsufficientSecurity as u8) => Ok(Self::InsufficientSecurity),
            x if x == (Self::InternalError as u8) => Ok(Self::InternalError),
            x if x == (Self::UserCanceled as u8) => Ok(Self::UserCanceled),
            x if x == (Self::NoRenegotiation as u8) => Ok(Self::NoRenegotiation),
            _ => Err(BufError::UnexpectedValue),
        }
    }
}

/// An alert following [Section 7.2].
///
/// [Section 7.2]: https://datatracker.ietf.org/doc/html/rfc2246#section-7.2
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Alert {
    /// The level of the alert.
    pub level: AlertLevel,
    /// The description of the alert.
    pub description: AlertDescription,
}

impl Codec for Alert {
    fn encode<W: BufMut>(&self, writer: &mut Cursor<W>, _: ()) -> BufResult<()> {
        self.level.encode(writer, ())?;
        self.description.encode(writer, ())
    }

    fn decode<R: Buf>(reader: &mut Cursor<R>, _: ()) -> BufResult<Self> {
        let level = AlertLevel::decode(reader, ())?;
        let description = AlertDescription::decode(reader, ())?;
        Ok(Self { level, description })
    }
}

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

    use super::{Alert, AlertDescription, AlertLevel};
    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 alert() {
        let etalon_bytes = &[0x02, 0x14]; // fatal, bad_record_mac
        let etalon_struct = Alert {
            level: AlertLevel::Fatal,
            description: AlertDescription::BadRecordMac,
        };

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