Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  post_handshake.rs   Sprache: Rust

 

//! Payloads exchanged during the post-handshake phase.
use educe::Educe;
use libthreema_macros::{DebugVariantNames, Name, VariantNames};
use tracing::error;

use crate::{
    common::{CspDeviceId, MessageFlags, MessageId, ThreemaId},
    csp::{
        CspProtocolError, CspProtocolInternalErrorCause,
        payload::{FrameEncoder, OutgoingFrame},
    },
    utils::{
        bytes::{ByteReader, ByteWriter, OwnedVecByteReader, OwnedVecByteWriter},
        debug::{Name as _, debug_slice_length},
        frame::{
            FrameDelimiter as _, LittleEndianDelimiterU16, VariableLengthFrameDecoderU16LittleEndianDelimited,
        },
    },
};

/// The payload of an echo request to be answered by a corresponding echo response.
#[derive(Clone, Name)]
pub struct EchoPayload(pub Vec<u8>);
impl EchoPayload {
    fn decode(reader: OwnedVecByteReader) -> Self {
        Self(reader.read_remaining_owned())
    }

    fn length(&self) -> usize {
        self.0.len()
    }

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        writer
            .write(&self.0)
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;
        Ok(())
    }
}

/// An end-to-end encrypted Threema message with additional end-to-end encrypted metadata.
///
/// Note 1: For incoming messages, only essential parts for the CSP layer have been decoded here, so that it
/// can be acknowledged at need. The E2E layer is responsible for decoding and decrypting the rest.
///
/// Note 2: For outgoing messages, the E2E layer is responsible for encoding the message bytes.
#[derive(Educe, Name)]
#[educe(Debug)]
#[cfg_attr(test, derive(Clone))]
pub struct MessageWithMetadataBox {
    /// The sender's Threema ID.
    pub sender_identity: ThreemaId,

    /// The ID of the message.
    pub message_id: MessageId,

    /// Message flags of the message.
    pub flags: MessageFlags,

    /// Raw bytes of the payload.
    #[educe(Debug(method(debug_slice_length)))]
    pub bytes: Vec<u8>,
}
impl MessageWithMetadataBox {
    fn decode(mut reader: OwnedVecByteReader) -> Result<Self, CspProtocolError> {
        // Truncate to the payload, since we need the raw bytes later
        reader.truncate();

        // Decode the sender's identity, message ID and flags
        let (sender_identity, message_id, flags) = reader
            .run(|reader| {
                let sender_identity = reader.read_fixed::<{ ThreemaId::LENGTH }>()?;
                reader.skip(ThreemaId::LENGTH)?;
                let message_id = MessageId(reader.read_u64_le()?);
                reader.skip(4)?;
                let message_flags = MessageFlags(reader.read_u8()?);
                Ok((sender_identity, message_id, message_flags))
            })
            .map_err(|error| CspProtocolError::DecodingFailed {
                name: Self::NAME,
                source: error,
            })?;

        let bytes = reader.into_inner();
        Ok(Self {
            sender_identity: ThreemaId::try_from(sender_identity.as_ref()).map_err(|error| {
                CspProtocolError::InvalidMessage {
                    name: Self::NAME,
                    cause: error.to_string(),
                }
            })?,
            message_id,
            flags,
            bytes,
        })
    }

    fn length(&self) -> usize {
        self.bytes.len()
    }

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        // TODO(LIB-31): This additional copying could be avoided if we are fine with layer violations. We
        // could split this payload into two, keep the incoming variant as-is but add an outgoing one where we
        // reference `OutgoingMessageWithMetadataBox` instead. Then we could call
        // `OutgoingMessageWithMetadataBox::encode_into` directly.
        writer
            .write(&self.bytes)
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;
        Ok(())
    }
}
#[cfg(test)]
impl TryFrom<Vec<u8>> for MessageWithMetadataBox {
    type Error = CspProtocolError;

    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
        Self::decode(OwnedVecByteReader::new(bytes))
    }
}

/// Acknowledges that a message has been received.
#[derive(Debug, Name)]
#[cfg_attr(test, derive(PartialEq))]
pub struct MessageAck {
    /// Identity of the sender (for an incoming message) or the receiver (for an outgoing message).
    pub sender_identity: ThreemaId,

    /// Refers to the message id of the acknowledged message.
    pub message_id: MessageId,
}
impl MessageAck {
    const LENGTH: usize = ThreemaId::LENGTH + MessageId::LENGTH;

    fn decode(reader: impl ByteReader) -> Result<Self, CspProtocolError> {
        let (identity, message_id) = reader
            .run_owned(|mut reader| {
                let sender_identity = reader.read_fixed::<{ ThreemaId::LENGTH }>()?;
                let message_id = reader.read_u64_le()?;
                let _ = reader.expect_consumed()?;
                Ok((sender_identity, message_id))
            })
            .map_err(|error| CspProtocolError::DecodingFailed {
                name: Self::NAME,
                source: error,
            })?;

        Ok(Self {
            sender_identity: ThreemaId::try_from(identity.as_ref()).map_err(|error| {
                CspProtocolError::InvalidMessage {
                    name: Self::NAME,
                    cause: error.to_string(),
                }
            })?,
            message_id: MessageId(message_id),
        })
    }

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        writer
            .run(|writer| {
                writer.write(&self.sender_identity.to_bytes())?;
                writer.write_u64_le(self.message_id.0)
            })
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;
        Ok(())
    }
}

/// APNs server type.
#[derive(Clone, Copy)]
pub enum ApnsServerType {
    /// Production server of the APNs service.
    Production,
    /// Development server of the APNs service.
    Development,
}

#[derive(Clone, Copy)]
#[repr(u8)]
enum PushNotificationTokenType {
    None = 0x00,
    // LegacyApnsProduction = 0x01,
    // LegacyApnsDevelopment = 0x02,
    // LegacyApnsProductionWithContentAvailable = 0x03,
    // LegacyApnsDevelopmentWithContentAvailable = 0x04,
    ApnsProductionWithMutableContent = 0x05,
    ApnsDevelopmentWithMutableContent = 0x06,
    Fcm = 0x11,
    Hms = 0x13,
}
impl PushNotificationTokenType {
    const LENGTH: usize = 1;
}
impl From<ApnsServerType> for PushNotificationTokenType {
    fn from(r#type: ApnsServerType) -> Self {
        match r#type {
            ApnsServerType::Production => Self::ApnsProductionWithMutableContent,
            ApnsServerType::Development => Self::ApnsDevelopmentWithMutableContent,
        }
    }
}

/// Sets (or clears) the push notification token on the server.
#[derive(Name)]
pub enum PushNotificationToken {
    /// Clear any existing push notification token of this device.
    Clear,

    /// Set an APNs push notification token.
    ///
    /// Note: For readers with legacy knowledge, this is the one with the `mutable-content` key and the
    /// encryption key. No other variants are in use by modern clients.
    Apns {
        /// APNs server type.
        r#type: ApnsServerType,
        /// Bundle ID of the app (e.g. `ch.threema.iapp` for consumer).
        bundle_id: String,
        /// Payload encryption key (XSalsa20).
        encryption_key: [u8; 32],
        /// APNs device token.
        token: Vec<u8>,
    },

    /// Set an FCM push notification token.
    Fcm(String),

    /// Set an HMS push notification token.
    Hms {
        /// App ID (e.g. `103713829` for consumer).
        app_id: String,
        /// HMS push token.
        token: String,
    },
}
impl PushNotificationToken {
    // Decoding messages is awkward as the separated binary data may contain a pipe delimiter, making the
    // delimiter totally useless. Still, we need it for legacy reasons...
    const AWKWARD_DELIMITER: &'static [u8; 1] = b"|";

    fn length(&self) -> usize {
        PushNotificationTokenType::LENGTH.saturating_add(match self {
            Self::Clear => 0,
            Self::Apns {
                bundle_id,
                encryption_key,
                token,
                ..
            } => token
                .len()
                .saturating_add(Self::AWKWARD_DELIMITER.len())
                .saturating_add(bundle_id.len())
                .saturating_add(Self::AWKWARD_DELIMITER.len())
                .saturating_add(encryption_key.len()),
            Self::Fcm(token) => token.len(),
            Self::Hms { app_id, token } => app_id
                .len()
                .saturating_add(Self::AWKWARD_DELIMITER.len())
                .saturating_add(token.len()),
        })
    }

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        writer
            .run(|writer| match self {
                Self::Clear => writer.write_u8(PushNotificationTokenType::None as u8),
                Self::Apns {
                    r#type,
                    bundle_id,
                    encryption_key,
                    token,
                } => {
                    writer.write_u8(PushNotificationTokenType::from(*r#type) as u8)?;

                    // Note: We're lucky here since we only need to encode it. But if you need to decode,
                    // well... because both `encryption_key` and `token` are binary, it can contain a pipe
                    // delimiter, making the delimiter totally useless. You'll have to split off the 32 byte
                    // `encryption_key` and the delimiter from the end, then seek backwards for the next
                    // delimiter to be able to distinguish the `token` from the `bundle_id`.
                    writer.write(token)?;
                    writer.write(Self::AWKWARD_DELIMITER)?;
                    writer.write(bundle_id.as_bytes())?;
                    writer.write(Self::AWKWARD_DELIMITER)?;
                    writer.write(encryption_key)
                },
                Self::Fcm(token) => {
                    writer.write_u8(PushNotificationTokenType::Fcm as u8)?;

                    writer.write(token.as_bytes())
                },
                Self::Hms { app_id, token } => {
                    writer.write_u8(PushNotificationTokenType::Hms as u8)?;

                    writer.write(app_id.as_bytes())?;
                    writer.write(Self::AWKWARD_DELIMITER)?;
                    writer.write(token.as_bytes())
                },
            })
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;
        Ok(())
    }
}

/// Removes some specific device's push notification tokens or all device's existing push
/// notification tokens from the server.
#[derive(Name)]
pub enum DeletePushNotificationToken {
    /// Remove all device's push notification tokens.
    All,

    /// Remove push notification tokens of the provided device IDs.
    ///
    /// # Contract
    ///
    /// This list must not be empty.
    Devices(Vec<CspDeviceId>),
}
impl DeletePushNotificationToken {
    fn length(&self) -> usize {
        match self {
            Self::All => 0,
            Self::Devices(csp_device_ids) => csp_device_ids.len().saturating_mul(CspDeviceId::LENGTH),
        }
    }

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        match self {
            Self::All => Ok(()),
            Self::Devices(csp_device_ids) => {
                if csp_device_ids.is_empty() {
                    return Err(CspProtocolError::ContractViolation(const_format::formatcp!(
                        "Device list may not be empty for {}",
                        DeletePushNotificationToken::NAME
                    )));
                }
                writer
                    .run(|writer| {
                        for csp_device_id in csp_device_ids {
                            writer.write_u64_le(csp_device_id.0)?;
                        }
                        Ok(())
                    })
                    .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                        name: Self::NAME,
                        source: error,
                    })?;
                Ok(())
            },
        }
    }
}

/// Set the connection idle timeout (in minutes).
#[derive(Name)]
pub struct ConnectionIdleTimeout(pub u16);
impl ConnectionIdleTimeout {
    const LENGTH: usize = 2;

    fn encode_into(&self, writer: &mut impl ByteWriter) -> Result<(), CspProtocolError> {
        writer
            .write_u16_le(self.0)
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;
        Ok(())
    }
}

/// Indicates that the connection has experienced an unrecoverable error and must be closed.
#[derive(Name)]
pub struct CloseError {
    /// Indicates whether the client is allowed to reconnect automatically after the connection has
    /// been severed.
    pub can_reconnect: bool,

    /// Error message.
    pub message: String,
}
impl CloseError {
    fn decode(mut reader: OwnedVecByteReader) -> Result<Self, CspProtocolError> {
        // The client may automatically reconnect iff the first byte is not zero
        let can_reconnect = reader
            .read_u8()
            .map_err(|error| CspProtocolError::DecodingFailed {
                name: Self::NAME,
                source: error,
            })?
            != 0;

        // Parse and decode the message
        let message = String::from_utf8(reader.read_remaining_owned()).map_err(|error| {
            CspProtocolError::InvalidMessage {
                name: Self::NAME,
                cause: error.to_string(),
            }
        })?;
        Ok(Self {
            can_reconnect,
            message,
        })
    }
}

/// Generic alert that should be displayed in the client's user interface.
#[derive(Debug, Name)]
pub struct ServerAlert(pub String);
impl ServerAlert {
    fn decode(reader: OwnedVecByteReader) -> Result<Self, CspProtocolError> {
        // Parse and decode the message
        let message = String::from_utf8(reader.read_remaining_owned()).map_err(|error| {
            CspProtocolError::InvalidMessage {
                name: Self::NAME,
                cause: error.to_string(),
            }
        })?;
        Ok(Self(message))
    }
}

const PAYLOAD_HEADER_RESERVED: [u8; 3] = [0_u8; 3];

#[derive(Clone, Copy, strum::FromRepr, VariantNames, DebugVariantNames)]
#[repr(u8)]
enum IncomingPayloadType {
    EchoRequest = 0x00,
    EchoResponse = 0x80,
    MessageWithMetadataBox = 0x02,
    MessageAck = 0x81,
    QueueSendComplete = 0xd0,
    DeviceCookieChangeIndication = 0xd2,
    CloseError = 0xe0,
    Alert = 0xe1,
}

/// An incoming payload received from the chat server during the post-handshake phase.
#[derive(Name, VariantNames, DebugVariantNames)]
pub enum IncomingPayload {
    /// An echo request to be answered by a corresponding echo response.
    ///
    /// Can be used for connection keep-alive or RTT estimation.
    EchoRequest(EchoPayload),

    /// An echo response corresponding to an echo request.
    EchoResponse(EchoPayload),

    /// An incoming end-to-end encrypted Threema message with additional end-to-end encrypted metadata.
    MessageWithMetadataBox(MessageWithMetadataBox),

    /// Acknowledges that the referred outgoing message has been stored on the server.
    MessageAck(MessageAck),

    /// Indicates that the incoming message queue on the server has been fully transmitted to the client. A
    /// client should not disconnect prior to having received this payload.
    QueueSendComplete,

    /// Indicates to the client that a device cookie mismatch has been detected since the last time that the
    /// device cookie change indication has been cleared.
    DeviceCookieChangeIndication,

    /// Indicates that the connection has experienced an unrecoverable error and must be closed.
    CloseError(CloseError),

    /// Generic alert that should be displayed in the client's user interface.
    ServerAlert(ServerAlert),

    /// The payload type is not known, either due to server misbehavior or as consequence of running an old
    /// version of libthreema. This information might be helpful for debugging.
    UnknownPayload {
        /// The (unsupported) type of the payload.
        payload_type: u8,

        /// The length of the received container.
        length: usize,
    },
}
impl TryFrom<Vec<u8>> for IncomingPayload {
    type Error = CspProtocolError;

    fn try_from(data: Vec<u8>) -> Result<Self, CspProtocolError> {
        fn expect_empty_payload(
            reader: OwnedVecByteReader,
            r#type: IncomingPayloadType,
        ) -> Result<(), CspProtocolError> {
            let _ = reader
                .expect_consumed()
                .map_err(|error| CspProtocolError::DecodingFailed {
                    name: r#type.variant_name(),
                    source: error,
                })?;
            Ok(())
        }

        let mut reader = OwnedVecByteReader::new(data);
        let r#type = reader
            .run(|reader| {
                // Get the payload type
                let r#type = reader.read_u8()?;

                // Skip reserved bytes
                reader.skip(PAYLOAD_HEADER_RESERVED.len())?;

                Ok(r#type)
            })
            .map_err(|error| CspProtocolError::DecodingFailed {
                name: Self::NAME,
                source: error,
            })?;

        // Parse payload type
        let Some(r#type) = IncomingPayloadType::from_repr(r#type) else {
            return Ok(Self::UnknownPayload {
                payload_type: r#type,
                length: reader.remaining(),
            });
        };

        // Create the corresponding variant by decoding the payload accordingly
        Ok(match r#type {
            IncomingPayloadType::EchoRequest => Self::EchoRequest(EchoPayload::decode(reader)),
            IncomingPayloadType::MessageWithMetadataBox => {
                Self::MessageWithMetadataBox(MessageWithMetadataBox::decode(reader)?)
            },
            IncomingPayloadType::EchoResponse => Self::EchoResponse(EchoPayload::decode(reader)),
            IncomingPayloadType::MessageAck => Self::MessageAck(MessageAck::decode(reader)?),
            IncomingPayloadType::QueueSendComplete => {
                expect_empty_payload(reader, r#type)?;
                Self::QueueSendComplete
            },
            IncomingPayloadType::DeviceCookieChangeIndication => {
                expect_empty_payload(reader, r#type)?;
                Self::DeviceCookieChangeIndication
            },
            IncomingPayloadType::CloseError => Self::CloseError(CloseError::decode(reader)?),
            IncomingPayloadType::Alert => Self::ServerAlert(ServerAlert::decode(reader)?),
        })
    }
}

#[cfg(test)]
mod incoming_payload_tests {
    use libthreema_dependencies::data_encoding::HEXLOWER_PERMISSIVE;

    use super::IncomingPayload;
    use crate::common::{MessageFlags, MessageId, ThreemaId};

    #[test]
    fn valid_message() -> anyhow::Result<()> {
        let payload = HEXLOWER_PERMISSIVE.decode(
            b"\
                02000000304441354d453736304850543945574489aa9a7eaff77d96cb7327680100340000000000\
                00000000000000000000000000000000000000000000000000000000439039d79074fa4a0d0961d6\
                51af57b94b7245c4c686733aab55b7f2b049fa3a8a5fec982eaa27d37557beaadd802cfc2703d849\
                b2d718b0db179e3e3bcdbf3c2be997490a0349f2e4fbaa43712e263aab0c2c4a920182f01f810df0\
                63363191b26c2c6404c0e84e73a3e005e327589702878e259642e1cf3b29e36db1c6a258f55ea73c\
                842fddafffd76a3057c0c13b6881bccc6522a0edee793f586fcb9ec5b398eb3be0af1a8c6111fe46\
                3ed25d916e66bea54955ca3398e27cbae25bfb6c16e26f326ecf8a4ba81aef9312b59f612b9e3355\
                de6c14c0434dc195e0a03462fc95d836a7bca74bda61d59be8489a9fdd9e626e7cb7324ac0724b0a\
                42168a5bea525eaef17d3bf13bbd8551ab8c85f5892fa6ba9c32e01343c3bc8ed2ad59f54411de08\
                9b193dca452b9699dafe34d124dfe521a956cce4adf58902a4c7b8bcf3d4548848dd2f1bee",
        )?;
        let payload = IncomingPayload::try_from(payload)?;
        let IncomingPayload::MessageWithMetadataBox(message) = payload else {
            panic!("Expected IncomingPayload::MessageWithMetadataBox");
        };

        assert_eq!(message.sender_identity, ThreemaId::try_from("0DA5ME76")?);
        assert_eq!(message.message_id, MessageId::from_hex("89aa9a7eaff77d96")?);
        assert_eq!(message.flags, MessageFlags(MessageFlags::SEND_PUSH_NOTIFICATION));
        assert_eq!(message.bytes.len(), 393);

        Ok(())
    }
}

enum OutgoingPayloadType {
    EchoRequest = 0x00,
    EchoResponse = 0x80,
    MessageWithMetadataBox = 0x01,
    MessageAck = 0x82,
    UnblockIncomingMessages = 0x03,
    SetPushNotificationToken = 0x20,
    DeletePushNotificationToken = 0x25,
    SetConnectionIdleTimeout = 0x30,
    ClearDeviceCookieChangeIndiciation = 0xd3,
}

/// An outgoing payload.
#[derive(Name, VariantNames, DebugVariantNames)]
pub enum OutgoingPayload {
    /// An echo request to be answered by a corresponding echo response.
    ///
    /// Can be used for connection keep-alive or RTT estimation.
    EchoRequest(EchoPayload),

    /// An echo response corresponding to an echo request.
    EchoResponse(EchoPayload),

    /// An outgoing end-to-end encrypted Threema message with additional end-to-end encrypted metadata.
    MessageWithMetadataBox(MessageWithMetadataBox),

    /// Acknowledges that the referred incoming message has been received by the client.
    MessageAck(MessageAck),

    /// Unblock incoming messages from the server.
    UnblockIncomingMessages,

    /// Sets (or clears) the push notification token on the server for this device.
    SetPushNotificationToken(PushNotificationToken),

    /// Removes some specific device's push notification tokens or all device's existing push
    /// notification tokens from the server.
    DeletePushNotificationToken(DeletePushNotificationToken),

    /// Set the connection idle timeout.
    SetConnectionIdleTimeout(ConnectionIdleTimeout),

    /// Clear the flag that triggers sending of a device-cookie-change-indication.
    ClearDeviceCookieChangeIndiciation,
}
impl OutgoingPayload {
    const HEADER_LENGTH: usize = 1 + PAYLOAD_HEADER_RESERVED.len();

    /// Encode the payload.
    pub(crate) fn encode(&self) -> Result<Vec<u8>, CspProtocolError> {
        // Determine the payload type and length
        let (r#type, length) = match self {
            Self::EchoRequest(payload) => (OutgoingPayloadType::EchoRequest, payload.length()),
            Self::EchoResponse(payload) => (OutgoingPayloadType::EchoResponse, payload.length()),
            Self::MessageWithMetadataBox(payload) => {
                (OutgoingPayloadType::MessageWithMetadataBox, payload.length())
            },
            Self::MessageAck(_) => (OutgoingPayloadType::MessageAck, MessageAck::LENGTH),
            Self::UnblockIncomingMessages => (OutgoingPayloadType::UnblockIncomingMessages, 0),
            Self::SetPushNotificationToken(payload) => {
                (OutgoingPayloadType::SetPushNotificationToken, payload.length())
            },
            Self::DeletePushNotificationToken(payload) => {
                (OutgoingPayloadType::DeletePushNotificationToken, payload.length())
            },
            Self::SetConnectionIdleTimeout(_) => (
                OutgoingPayloadType::SetConnectionIdleTimeout,
                ConnectionIdleTimeout::LENGTH,
            ),
            Self::ClearDeviceCookieChangeIndiciation => {
                (OutgoingPayloadType::ClearDeviceCookieChangeIndiciation, 0)
            },
        };

        // Encode the header
        let mut writer = OwnedVecByteWriter::new_with_capacity(Self::HEADER_LENGTH.saturating_add(length));
        writer
            .run(|writer| {
                // Encode the payload type
                writer.write_u8(r#type as u8)?;

                // Encode reserved bytes
                writer.write(&PAYLOAD_HEADER_RESERVED)
            })
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: Self::NAME,
                source: error,
            })?;

        // Encode the payload
        match self {
            Self::EchoRequest(payload) | Self::EchoResponse(payload) => payload.encode_into(&style='color:red'>mut writer),
            Self::MessageWithMetadataBox(payload) => payload.encode_into(&>mut writer),
            Self::MessageAck(payload) => payload.encode_into(&mut writer),
            Self::SetPushNotificationToken(payload) => payload.encode_into(&mut writer),
            Self::DeletePushNotificationToken(payload) => payload.encode_into(&mut writer),
            Self::SetConnectionIdleTimeout(payload) => payload.encode_into(&mut writer),
            Self::UnblockIncomingMessages | Self::ClearDeviceCookieChangeIndiciation => Ok(()),
        }?;

        // Done
        Ok(writer.into_inner())
    }
}

/// Encrypted [`IncomingPayload`] frame decoder.
pub(crate) type PayloadDecoder = VariableLengthFrameDecoderU16LittleEndianDelimited;

/// Contains an [`OutgoingPayload`] to encode it.
#[derive(Name)]
pub(crate) struct EncryptedOutgoingPayload(pub(crate) Vec<u8>);
impl FrameEncoder for EncryptedOutgoingPayload {
    fn encode_frame(&self) -> Result<OutgoingFrame, CspProtocolError> {
        let mut writer = OwnedVecByteWriter::new_with_capacity(
            LittleEndianDelimiterU16::LENGTH.saturating_add(self.0.len()),
        );

        // Prepare length
        let length = self.0.len();
        let length = u16::try_from(length).map_err(|_| {
            error!(length, "Encoded frame length exceeds a u16");
            CspProtocolInternalErrorCause::from("Encoded frame too large, exceeds a u16")
        })?;

        // Encode the header and the content
        writer
            .run(|writer| {
                // Encode the length of the frame
                writer.write_u16_le(length)?;

                // Encode the content
                writer.write(&self.0)
            })
            .map_err(|error| CspProtocolInternalErrorCause::EncodingFailed {
                name: OutgoingFrame::NAME,
                source: error,
            })?;

        // Done
        Ok(OutgoingFrame(writer.into_inner()))
    }
}

Messung
CHZG
9210010097

¤ Dauer der Verarbeitung: 0.5 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=439737