Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
//! Payloads exchanged during the handshake phase.
use educe::Educe;
use libthreema_macros::Name;
use prost::Message as _;
use crate::{
common::keys::PublicKey,
d2m::{
D2mProtocolError,
payload::{PayloadDecoder, PayloadEncoder},
},
protobuf::d2m as protobuf,
utils::{
bytes::{ByteWriter, OwnedVecByteReader},
debug::{Name, debug_slice_length},
time::{ClockDelta, Duration},
},
};
/// Decoder for a specific D2M payload.
trait SpecificPayloadDecoder: Sized + Name {
const TYPE: u8;
fn decode(reader: OwnedVecByteReader) -> Result<Self, D2mProtocolError>;
}
impl<TDecoder: SpecificPayloadDecoder> PayloadDecoder for TDecoder {
fn decode(r#type: u8, reader: OwnedVecByteReader) -> Result<Self, D2mProtocolError> {
// Ensure the type matches the one we expect
if r#type != Self::TYPE {
return Err(D2mProtocolError::UnexpectedPayloadType(r#type));
}
// Decode payload
Self::decode(reader)
}
}
#[repr(u8)]
enum HandshakePayloadType {
ServerHello = 0x10,
ClientHello = 0x11,
ServerInfo = 0x12,
}
/// Initial message from the server, containing an authentication challenge.
#[derive(Name, Educe)]
#[educe(Debug)]
pub(crate) struct ServerHello {
/// Highest protocol version [`protobuf::ProtocolVersion`] the server supports.
pub(crate) version: u32,
/// Ephemeral Server Key (ESK).
pub(crate) ephemeral_server_key: PublicKey,
/// 32 byte random challenge
#[educe(Debug(method(debug_slice_length)))]
pub(crate) challenge: [u8; 32],
}
impl SpecificPayloadDecoder for ServerHello {
const TYPE: u8 = HandshakePayloadType::ServerHello as u8;
fn decode(reader: OwnedVecByteReader) -> Result<Self, D2mProtocolError> {
let protobuf::ServerHello {
version,
esk,
challenge,
} = Self::decode_protobuf(reader)?;
Ok(Self {
version,
ephemeral_server_key: PublicKey::try_from(esk.as_slice()).map_err(|_| {
D2mProtocolError::InvalidMessage {
name: Self::NAME,
cause: "Invalid ephemeral server key".to_owned(),
}
})?,
challenge: challenge
.try_into()
.map_err(|_| D2mProtocolError::InvalidMessage {
name: Self::NAME,
cause: "Invalid challenge length".to_owned(),
})?,
})
}
}
/// Initial message from the client, containing the authentication challenge response and additional login
/// information.
impl PayloadEncoder for protobuf::ClientHello {
#[inline]
fn type_and_length(&self) -> (u8, Option<usize>) {
(HandshakePayloadType::ClientHello as u8, Some(self.encoded_len()))
}
#[inline]
fn encode_into<TWriter: ByteWriter>(self, writer: &mut TWriter) -> Result<(), D2mProtocolError> {
Self::encode_protobuf(self, writer)
}
}
/// Parts of the server's configuration and the device slot state.
#[derive(Name, Educe)]
#[educe(Debug)]
pub struct ServerInfo {
/// Clock delta between the server's time and the client's time.
///
/// If the client's current timestamp deviates by more than 20 minutes, the client should disconnect and
/// prompt the user to synchronise its clock. The user should also have an option to _connect anyway_
/// which should be cached for a reasonable amount of time.
pub clock_delta: ClockDelta,
/// Maximum number of available device slots.
pub max_device_slots: u32,
/// Device slot state of the client on the server.
pub device_slot_state: Option<protobuf::DeviceSlotState>,
/// Device data shared among devices (`SharedDeviceData`), encrypted by `DGSDDK.secret` and prefixed with
/// a random nonce.
#[educe(Debug(method(debug_slice_length)))]
pub encrypted_shared_device_data: Vec<u8>,
/// Amount of messages in the reflection queue that will now be sent to the device. If the client is
/// up-to-date, the value will be 0.
///
/// Note: The amount of messages in the reflection queue may increase at any time, so there is no
/// guarantee that `ReflectionQueueDry` will be received after having received `reflection_queue_length`
/// reflected messages.
pub reflection_queue_length: u32,
}
impl SpecificPayloadDecoder for ServerInfo {
const TYPE: u8 = HandshakePayloadType::ServerInfo as u8;
fn decode(reader: OwnedVecByteReader) -> Result<Self, D2mProtocolError> {
let protobuf::ServerInfo {
current_time,
max_device_slots,
device_slot_state,
encrypted_shared_device_data,
reflection_queue_length,
} = Self::decode_protobuf(reader)?;
Ok(Self {
clock_delta: ClockDelta::calculate(Duration::from_millis(current_time)),
max_device_slots,
device_slot_state: protobuf::DeviceSlotState::try_from(device_slot_state).ok(),
encrypted_shared_device_data,
reflection_queue_length,
})
}
}